/home/ivoiecob/email.hirewise-va.com/vendor/afterlogic/dav/lib/DAV/CardDAV/Backend/PDO.php
<?php
/**
 * This code is licensed under AGPLv3 license or Afterlogic Software License
 * if commercial version of the product was purchased.
 * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
 */

namespace Afterlogic\DAV\CardDAV\Backend;

use Afterlogic\DAV\Constants;
use Aurora\Modules\Contacts\Models\ContactCard;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Reader;

/**
 * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
 * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
 * @copyright Copyright (c) 2019, Afterlogic Corp.
 */
class PDO extends \Sabre\CardDAV\Backend\PDO
{
    /**
     * PDO connection.
     *
     * @var \PDO
     */
    protected $pdo;

    protected $contactsCardsTableName;

    protected $sharedAddressBooksTableName;

    private string $cardsPropertiesTableName = '';

    protected function getTenantPrincipal($sUserPublicId)
    {
        $sTenantPrincipal = 'default_' . \Afterlogic\DAV\Constants::DAV_TENANT_PRINCIPAL;
        $oUser = \Aurora\System\Api::GetModuleDecorator('Core')->GetUserByPublicId($sUserPublicId);
        if ($oUser) {
            $sTenantPrincipal = $oUser->IdTenant . '_' . \Afterlogic\DAV\Constants::DAV_TENANT_PRINCIPAL;
        }

        return \Afterlogic\DAV\Constants::PRINCIPALS_PREFIX . $sTenantPrincipal;
    }


    /**
     * Sets up the object
     */
    public function __construct()
    {
        parent::__construct(\Aurora\System\Api::GetPDO());
        $sDbPrefix = \Aurora\System\Api::GetSettings()->DBPrefix;
        $this->cardsTableName = $sDbPrefix.Constants::T_CARDS;
        $this->addressBooksTableName = $sDbPrefix.Constants::T_ADDRESSBOOKS;
        $this->addressBookChangesTableName = $sDbPrefix.Constants::T_ADDRESSBOOKCHANGES;
        $this->contactsCardsTableName = $sDbPrefix.'contacts_cards';
        $this->sharedAddressBooksTableName = $sDbPrefix.'adav_shared_addressbooks';
    }

    /**
     * Returns the addressbook for a specific user.
     *
     * @param string $principalUri
     * @param string $addressbookUri
     * @return array
     */
    public function getAddressBookForUser($principalUri, $addressbookUri)
    {
        $mAddressBook = false;

        $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, synctoken FROM '.$this->addressBooksTableName.' WHERE principaluri = ? AND uri = ?');
        $stmt->execute(array($principalUri, $addressbookUri));

        $row = $stmt->fetch(\PDO::FETCH_ASSOC);

        if ($row) {
            $mAddressBook = [
                'id'                                                          => $row['id'],
                'uri'                                                         => $row['uri'],
                'principaluri'                                                => $row['principaluri'],
                '{DAV:}displayname'                                           => $row['displayname'],
                '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
                '{http://calendarserver.org/ns/}getctag'                      => $row['synctoken'],
                '{http://sabredav.org/ns}sync-token'                          => $row['synctoken'] ? $row['synctoken'] : '0',
            ];
        }

        return $mAddressBook;
    }

    /**
     * Returns all cards for a specific addressbook id.
     *
     * @return array
     */
    public function getCardsSharedToAll($addressbookId)
    {
        $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?');
        $stmt->execute(array($addressbookId));

        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
    }

    public function getSharedWithAllAddressBook($sPrincipalUri)
    {
        $sTenantPrincipal = $this->getTenantPrincipal(basename($sPrincipalUri));

        $aAddressBook = $this->getAddressBookForUser($sTenantPrincipal, \Afterlogic\DAV\Constants::ADDRESSBOOK_SHARED_WITH_ALL_NAME);

        if (!is_array($aAddressBook)) {
            $sProperties = [
                '{DAV:}displayname' => \Afterlogic\DAV\Constants::ADDRESSBOOK_SHARED_WITH_ALL_DISPLAY_NAME
            ];

            $this->createAddressBook($sTenantPrincipal, \Afterlogic\DAV\Constants::ADDRESSBOOK_SHARED_WITH_ALL_NAME, $sProperties);
            $aAddressBook = $this->getAddressBookForUser($sTenantPrincipal, \Afterlogic\DAV\Constants::ADDRESSBOOK_SHARED_WITH_ALL_NAME);
        }

        return $aAddressBook;
    }

    /**
     * Returns a list of cards.
     *
     * This method should work identical to getCard, but instead return all the
     * cards in the list as an array.
     *
     * If the backend supports this, it may allow for some speed-ups.
     *
     * @param array $uris
     * @return array
     */
    public function getMultipleSharedWithAllCards(array $uris)
    {
        $query = 'SELECT id, uri, lastmodified, etag, size, carddata FROM ' . $this->cardsTableName . ' WHERE uri IN (';
        // Inserting a whole bunch of question marks
        $query .= implode(',', array_fill(0, count($uris), '?'));
        $query .= ')';

        $stmt = $this->pdo->prepare($query);
        $stmt->execute($uris);
        $result = [];
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
            $row['etag'] = '"' . $row['etag'] . '"';
            $row['lastmodified'] = (int)$row['lastmodified'];
            $result[] = $row;
        }
        return $result;
    }

    public function getSharedAddressBooks($principalUri)
    {
        $sDbPrefix = \Aurora\System\Api::GetSettings()->DBPrefix;
        $stmt = $this->pdo->prepare('SELECT ab.id, sab.addressbookuri as uri, ab.displayname, ab.principaluri, ab.description, ab.synctoken, sab.access, sab.group_id
        FROM ' . $sDbPrefix . 'adav_addressbooks as ab, ' . $sDbPrefix . 'adav_shared_addressbooks as sab
        WHERE ab.id = sab.addressbook_id AND sab.principaluri = ?');
        $stmt->execute([$principalUri]);

        $addressBooks = [];

        foreach ($stmt->fetchAll() as $row) {
            $addressBooks[] = [
                'id' => $row['id'],
                'uri' => $row['uri'],
                'principaluri' => $row['principaluri'],
                '{DAV:}displayname' => $row['displayname'] . ' (' . basename($row['principaluri']) . ')',
                '{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => $row['description'],
                '{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
                '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0',
                'access' => $row['access'],
                'group_id' => $row['group_id']
            ];
        }

        return $addressBooks;
    }

    public function createCard($addressBookId, $cardUri, $cardData)
    {
        $result = parent::createCard($addressBookId, $cardUri, $cardData);
        $this->updateProperties($addressBookId, $cardUri, $cardData);

        return $result;
    }

    public function updateCard($addressBookId, $cardUri, $cardData)
    {
        $result = parent::updateCard($addressBookId, $cardUri, $cardData);
        $this->updateProperties($addressBookId, $cardUri, $cardData);

        return $result;
    }

    public function deleteCard($addressBookId, $cardUri)
    {
        $cardId = $this->getCardId($addressBookId, $cardUri);
        ContactCard::where('AddressBookId', $addressBookId)->where('CardId', $cardId)->delete();
        $this->purgeProperties($addressBookId, $cardUri);
        return parent::deleteCard($addressBookId, $cardUri);
    }

    /**
     * Get ID from a given contact
     */
    protected function getCardId(int $addressBookId, string $uri): int
    {
        $stmt = $this->pdo->prepare('SELECT id FROM ' . $this->cardsTableName . ' WHERE uri = ? AND addressbookid = ?');
        $stmt->execute([$uri, $addressBookId]);
        $cardIds = $stmt->fetch();

        if (!isset($cardIds['id'])) {
            throw new \InvalidArgumentException('Card does not exists: ' . $uri);
        }

        return (int) $cardIds['id'];
    }

    /**
     * update properties table
     *
     * @param int $addressBookId
     * @param string $cardUri
     * @param string $vCardSerialized
     */
    public function updateProperties($addressBookId, $cardUri, $vCardData)
    {
        $vCard = $this->readCard($vCardData);
        $cardId = $this->getCardId($addressBookId, $cardUri);
        $contactCard = ContactCard::firstOrNew(['CardId' => $cardId]);

        $contactCard->AddressBookId = $addressBookId;
        foreach ($vCard->children() as $property) {
            switch ($property->name) {
                case 'FN':
                    $contactCard->FullName = $property->getValue();
                    break;
                case 'N':
                    $nameParts = $property->getParts();
                    $contactCard->FirstName = $nameParts[0];
                    $contactCard->LastName = $nameParts[1];
                    break;
                case 'EMAIL':
                    $type = $property['TYPE'];
                    if ($type) {
                        if ($type->has('WORK') || $type->has('INTERNET')) {
                            $contactCard->BusinessEmail = (string) $property;
                            if ($type->has('PREF')) {
                                $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Business;
                                $contactCard->ViewEmail = $contactCard->BusinessEmail;
                            }
                        } elseif ($type->has('HOME')) {
                            $contactCard->PersonalEmail = (string) $property;
                            if ($type->has('PREF')) {
                                $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Personal;
                                $contactCard->ViewEmail = $contactCard->PersonalEmail;
                            }
                        } elseif ($type->has('OTHER')) {
                            $contactCard->OtherEmail = (string) $property;
                            if ($type->has('PREF')) {
                                $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Other;
                                $contactCard->ViewEmail = $contactCard->OtherEmail;
                            }
                        } elseif ($property->group && isset($vCard->{$property->group.'.X-ABLABEL'}) &&
                            strtolower((string) $vCard->{$property->group.'.X-ABLABEL'}) === '_$!<other>!$_') {
                            $contactCard->OtherEmail = (string) $property;
                            if ($type->has('PREF')) {
                                $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Other;
                                $contactCard->ViewEmail = $contactCard->OtherEmail;
                            }
                        }
                    } else {
                        $contactCard->OtherEmail = (string) $property;
                        $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Other;
                        $contactCard->ViewEmail = $contactCard->OtherEmail;
                    }
                    if (empty($contactCard->PrimaryEmail)) {
                        if (!empty($contactCard->BusinessEmail)) {
                            $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Business;
                            $contactCard->ViewEmail = $contactCard->BusinessEmail;
                        } elseif (!empty($contactCard->PersonalEmail)) {
                            $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Personal;
                            $contactCard->ViewEmail = $contactCard->PersonalEmail;
                        } elseif (!empty($contactCard->OtherEmail)) {
                            $contactCard->PrimaryEmail = \Aurora\Modules\Contacts\Enums\PrimaryEmail::Other;
                            $contactCard->ViewEmail = $contactCard->OtherEmail;
                        }
                    }
                    break;
                case 'KIND':
                case 'X-ADDRESSBOOKSERVER-KIND':
                    if (strtoupper($property->getValue()) === 'GROUP') {
                        $contactCard->IsGroup = true;
                    }
                    break;
                case 'X-FREQUENCY':
                    $contactCard->Frequency = (int) $property->getValue();
            }
        }
        $contactCard->save();
    }

    /**
     * read vCard data into a vCard object
     *
     * @param string $cardData
     * @return VCard
     */
    protected function readCard($cardData)
    {
        return Reader::read($cardData);
    }

    /**
     * delete all properties from a given card
     *
     * @param int $addressBookId
     * @param int $cardId
     */
    protected function purgeProperties($addressBookId, $cardUri)
    {
        $cardId = $this->getCardId($addressBookId, $cardUri);
        $stmt = $this->pdo->prepare('DELETE FROM ' . $this->contactsCardsTableName . ' WHERE addressbookid = ? AND cardid = ?');
        $stmt->execute([$addressBookId, $cardId]);
    }

    /**
     * Deletes an entire addressbook and all its contents.
     *
     * @param int $addressBookId
     */
    public function deleteAddressBook($addressBookId)
    {
        parent::deleteAddressBook($addressBookId);

        $stmt = $this->pdo->prepare('DELETE FROM '.$this->contactsCardsTableName.' WHERE AddressBookId = ?');
        $stmt->execute([$addressBookId]);

        $sharedAddressBooksTableExists = false;
        try {
            $this->pdo->query('SELECT 1 FROM ' . $this->sharedAddressBooksTableName);
            $sharedAddressBooksTableExists = true;
        } catch (\PDOException $e){
            $sharedAddressBooksTableExists = false;
        }
        
        if ($sharedAddressBooksTableExists) {
            $stmt = $this->pdo->prepare('DELETE FROM '.$this->sharedAddressBooksTableName.' WHERE addressbook_id = ?');
            $stmt->execute([$addressBookId]);
        }
    }

    /**
     * Returns the addressbook for a specific user.
     *
     * @param string $principalUri
     * @param int $addressbookId
     * @return array|bool
     */
    public function getAddressBookByIdForUser($principalUri, $addressbookId)
    {
        $mAddressBook = false;

        $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, synctoken FROM '.$this->addressBooksTableName.' WHERE principaluri = ? AND id = ?');
        $stmt->execute(array($principalUri, $addressbookId));

        $row = $stmt->fetch(\PDO::FETCH_ASSOC);

        if ($row) {
            $mAddressBook = [
                'id'                                                          => $row['id'],
                'uri'                                                         => $row['uri'],
                'principaluri'                                                => $row['principaluri'],
                '{DAV:}displayname'                                           => $row['displayname'],
                '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
                '{http://calendarserver.org/ns/}getctag'                      => $row['synctoken'],
                '{http://sabredav.org/ns}sync-token'                          => $row['synctoken'] ? $row['synctoken'] : '0',
            ];
        }

        return $mAddressBook;
    }

    /**
     * Returns the addressbook by a specific id.
     *
     * @param int $addressbookId
     * @return array|bool
     */
    public function getAddressBookById($addressbookId)
    {
        $mAddressBook = false;

        $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, synctoken FROM '.$this->addressBooksTableName.' WHERE id = ?');
        $stmt->execute(array($addressbookId));

        $row = $stmt->fetch(\PDO::FETCH_ASSOC);

        if ($row) {
            $mAddressBook = [
                'id'                                                          => $row['id'],
                'uri'                                                         => $row['uri'],
                'principaluri'                                                => $row['principaluri'],
                '{DAV:}displayname'                                           => $row['displayname'],
                '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
                '{http://calendarserver.org/ns/}getctag'                      => $row['synctoken'],
                '{http://sabredav.org/ns}sync-token'                          => $row['synctoken'] ? $row['synctoken'] : '0',
            ];
        }

        return $mAddressBook;
    }

    public function updateCardAddressBook($addressBookId, $newAddressBookId, $cardUri)
    {
        $result = false;
        $card = $this->getCard($addressBookId, $cardUri);
        if ($card) {
            $stmt = $this->pdo->prepare('UPDATE '.$this->cardsTableName.' SET lastmodified = ?, addressbookid = ? WHERE uri = ? AND addressbookid = ?');
            $stmt->execute([
                time(),
                $newAddressBookId,
                $cardUri,
                $addressBookId,
            ]);
    
            ContactCard::where('CardId', $card['id'])->update(['AddressBookId' => $newAddressBookId]);
            
            $this->addChange($addressBookId, $cardUri, 3);
            $this->addChange($newAddressBookId, $cardUri, 1);

            $result = true;
        }

        return $result;
    }
}