sDsn = $sDsn; $this->sUser = $sUser; $this->sPassword = $sPassword; $this->sDsnType = $sDsnType; $this->bConsiderShare = true; $this->bExplain = false; } /** * @param bool $bConsiderShare * * @return \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook */ public function ConsiderShare($bConsiderShare = true) { $this->bConsiderShare = !!$bConsiderShare; return $this; } /** * @return bool */ public function IsSupported() { $aDrivers = \class_exists('PDO') ? \PDO::getAvailableDrivers() : array(); return \is_array($aDrivers) ? \in_array($this->sDsnType, $aDrivers) : false; } /** * @return bool */ public function IsConsiderShare() { return $this->bConsiderShare; } /** * @return bool */ public function IsSharingAllowed() { return $this->IsConsiderShare() && $this->IsSupported(); } /** * @param string $sEmail * @return mixed */ public function GetUserUidByEmail($sEmail) { $this->Sync(); $iId = $this->getUserId($sEmail); return 0 < $iId ? (string) $iId : ''; } /** * @param string $sEmail * @return string */ public function GetCtagByEmail($sEmail) { $this->Sync(); $sResult = '0'; $iUserID = $this->getUserId($sEmail); if (0 < $iUserID) { $oStmt = $this->prepareAndExecute('SELECT MAX(id_prop) as max_value FROM rainloop_pab_properties WHERE id_user = :id_user', array(':id_user' => array($iUserID, \PDO::PARAM_INT))); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if ($aFetch && !empty($aFetch[0]['max_value'])) { $sResult = 'RL-CTAG-'.((string) $aFetch[0]['max_value']); } } } return $sResult; } /** * @param string $sEmail * @param bool $bCreate = false * * @return string */ public function GetUserHashByEmail($sEmail, $bCreate = false) { $this->Sync(); $sHash = ''; $iUserID = $this->getUserId($sEmail); if (0 < $iUserID) { $oStmt = $this->prepareAndExecute('SELECT pass_hash FROM rainloop_pab_users WHERE id_user = :id_user LIMIT 1', array(':id_user' => array($iUserID, \PDO::PARAM_INT))); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if ($aFetch && !empty($aFetch[0]['pass_hash'])) { $sHash = \rtrim(\base_convert(\md5(\md5($sEmail.'-'.\trim($aFetch[0]['pass_hash']).'-rainloop')), 16, 32), '0'); } else if ($bCreate) { $this->prepareAndExecute('INSERT INTO rainloop_pab_users (id_user, email, pass_hash) VALUES (:id_user, :email, :pass_hash);', array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':email' => array($sEmail, \PDO::PARAM_STR), ':pass_hash' => array(\md5($sEmail.\microtime(true)), \PDO::PARAM_STR) ) ); $sHash = $this->GetUserHashByEmail($sEmail, false); } } } return $sHash; } /** * @param string $sEmail * @param \RainLoop\Providers\PersonalAddressBook\Classes\Contact $oContact * * @return bool */ public function ContactSave($sEmail, &$oContact) { $this->Sync(); $iUserID = $this->getUserId($sEmail); $iIdContact = 0 < \strlen($oContact->IdContact) && \is_numeric($oContact->IdContact) ? (int) $oContact->IdContact : 0; $bUpdate = 0 < $iIdContact; if (!$this->bConsiderShare) { $oContact->ScopeType = \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_; } $oContact->UpdateDependentValues(); $oContact->Changed = \time(); try { if ($this->isTransactionSupported()) { $this->beginTransaction(); } $aFreq = array(); if ($bUpdate) { $aFreq = $this->getContactFreq($iUserID, $iIdContact); $sSql = 'UPDATE rainloop_pab_contacts SET id_contact_str = :id_contact_str, display = :display, '. 'scope_type = :scope_type, changed = :changed, '. 'carddav_data = :carddav_data, carddav_hash = :carddav_hash, carddav_size = :carddav_size '. 'WHERE id_user = :id_user AND id_contact = :id_contact'; $this->prepareAndExecute($sSql, array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':id_contact' => array($iIdContact, \PDO::PARAM_INT), ':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR), ':display' => array($oContact->Display, \PDO::PARAM_STR), ':scope_type' => array($oContact->ScopeType, \PDO::PARAM_INT), ':changed' => array($oContact->Changed, \PDO::PARAM_INT), ':carddav_data' => array($oContact->CardDavData, \PDO::PARAM_STR), ':carddav_hash' => array($oContact->CardDavHash, \PDO::PARAM_STR), ':carddav_size' => array($oContact->CardDavSize, \PDO::PARAM_INT) ) ); // clear previos props $this->prepareAndExecute( 'DELETE FROM rainloop_pab_properties WHERE id_user = :id_user AND id_contact = :id_contact', array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':id_contact' => array($iIdContact, \PDO::PARAM_INT) ) ); } else { $sSql = 'INSERT INTO rainloop_pab_contacts '. '( id_user, id_contact_str, display, scope_type, changed, carddav_data, carddav_hash, carddav_size) VALUES '. '(:id_user, :id_contact_str, :display, :scope_type, :changed, :carddav_data, :carddav_hash, :carddav_size)'; $this->prepareAndExecute($sSql, array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR), ':display' => array($oContact->Display, \PDO::PARAM_STR), ':scope_type' => array($oContact->ScopeType, \PDO::PARAM_INT), ':changed' => array($oContact->Changed, \PDO::PARAM_INT), ':carddav_data' => array($oContact->CardDavData, \PDO::PARAM_STR), ':carddav_hash' => array($oContact->CardDavHash, \PDO::PARAM_STR), ':carddav_size' => array($oContact->CardDavSize, \PDO::PARAM_INT) ) ); $sLast = $this->lastInsertId('id_contact'); if (\is_numeric($sLast) && 0 < (int) $sLast) { $iIdContact = (int) $sLast; $oContact->IdContact = (string) $iIdContact; } } if (0 < $iIdContact) { $aParams = array(); foreach ($oContact->Properties as /* @var $oProp \RainLoop\Providers\PersonalAddressBook\Classes\Property */ $oProp) { $iFreq = $oProp->Frec; if ($oProp->IsEmail() && isset($aFreq[$oProp->Value])) { $iFreq = $aFreq[$oProp->Value]; } $aParams[] = array( ':id_contact' => array($iIdContact, \PDO::PARAM_INT), ':id_user' => array($iUserID, \PDO::PARAM_INT), ':scope_type' => array($oContact->ScopeType, \PDO::PARAM_INT), ':prop_type' => array($oProp->Type, \PDO::PARAM_INT), ':prop_type_custom' => array($oProp->TypeCustom, \PDO::PARAM_STR), ':prop_value' => array($oProp->Value, \PDO::PARAM_STR), ':prop_value_custom' => array($oProp->ValueCustom, \PDO::PARAM_STR), ':prop_frec' => array($iFreq, \PDO::PARAM_INT), ); } $sSql = 'INSERT INTO rainloop_pab_properties '. '( id_contact, id_user, prop_type, prop_type_custom, prop_value, prop_value_custom, scope_type, prop_frec) VALUES '. '(:id_contact, :id_user, :prop_type, :prop_type_custom, :prop_value, :prop_value_custom, :scope_type, :prop_frec)'; $this->prepareAndExecute($sSql, $aParams, true); } } catch (\Exception $oException) { if ($this->isTransactionSupported()) { $this->rollBack(); } throw $oException; } if ($this->isTransactionSupported()) { $this->commit(); } return 0 < $iIdContact; } /** * @param string $sEmail * @param array $aContactIds * * @return bool */ public function DeleteContacts($sEmail, $aContactIds) { $this->Sync(); $iUserID = $this->getUserId($sEmail); $aContactIds = \array_filter($aContactIds, function (&$mItem) { $mItem = (int) \trim($mItem); return 0 < $mItem; }); if (0 === \count($aContactIds)) { return false; } $sIDs = \implode(',', $aContactIds); $aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT)); $this->prepareAndExecute('DELETE FROM rainloop_pab_tags_contacts WHERE id_contact IN ('.$sIDs.')'); $this->prepareAndExecute('DELETE FROM rainloop_pab_properties WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams); $this->prepareAndExecute('DELETE FROM rainloop_pab_contacts WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams); return true; } /** * @param string $sEmail * @param array $aTagsIds * * @return bool */ public function DeleteTags($sEmail, $aTagsIds) { $this->Sync(); $iUserID = $this->getUserId($sEmail); $aTagsIds = \array_filter($aTagsIds, function (&$mItem) { $mItem = (int) \trim($mItem); return 0 < $mItem; }); if (0 === \count($aTagsIds)) { return false; } $sIDs = \implode(',', $aTagsIds); $aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT)); $this->prepareAndExecute('DELETE FROM rainloop_pab_tags_contacts WHERE id_tag IN ('.$sIDs.')'); $this->prepareAndExecute('DELETE FROM rainloop_pab_tags WHERE id_user = :id_user AND id_tag IN ('.$sIDs.')', $aParams); return true; } /** * @param string $sEmail * @param int $iOffset = 0 * @param int $iLimit = 20 * @param string $sSearch = '' * @param int $iResultCount = 0 * * @return array */ public function GetContacts($sEmail, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0) { $this->Sync(); $iOffset = 0 <= $iOffset ? $iOffset : 0; $iLimit = 0 < $iLimit ? (int) $iLimit : 20; $sSearch = \trim($sSearch); $iUserID = $this->getUserId($sEmail); $iCount = 0; $aSearchIds = array(); $aPropertyFromSearchIds = array(); if (0 < \strlen($sSearch)) { $sSql = 'SELECT id_user, id_prop, id_contact FROM rainloop_pab_properties '. 'WHERE ('. 'id_user = :id_user'. ($this->bConsiderShare ? ' OR scope_type = :scope_type_share_all' : ''). ') AND (prop_value LIKE :search ESCAPE \'=\' OR ('. 'prop_type IN ('.\implode(',', array( PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES )).') AND prop_value_custom LIKE :search_custom_phone'. ')) GROUP BY id_contact, id_prop'; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':search' => array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR), ':search_custom_phone' => array($this->specialConvertSearchValueCustomPhone($sSearch), \PDO::PARAM_STR) ); if ($this->bConsiderShare) { $aParams[':scope_type_share_all'] = array(\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL, \PDO::PARAM_INT); } $oStmt = $this->prepareAndExecute($sSql, $aParams); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { $iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0; if (0 < $iIdContact) { $aSearchIds[] = $iIdContact; $aPropertyFromSearchIds[$iIdContact] = isset($aItem['id_prop']) ? (int) $aItem['id_prop'] : 0; } } } $iCount = \count($aSearchIds); } } else { $sSql = 'SELECT COUNT(DISTINCT id_contact) as contact_count FROM rainloop_pab_properties '. 'WHERE id_user = :id_user'. ($this->bConsiderShare ? ' OR scope_type = :scope_type_share_all' : '') ; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT) ); if ($this->bConsiderShare) { $aParams[':scope_type_share_all'] = array(\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL, \PDO::PARAM_INT); } $oStmt = $this->prepareAndExecute($sSql, $aParams); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if ($aFetch && isset($aFetch[0]['contact_count']) && is_numeric($aFetch[0]['contact_count']) && 0 < (int) $aFetch[0]['contact_count']) { $iCount = (int) $aFetch[0]['contact_count']; } } } $iResultCount = $iCount; if (0 < $iCount) { $sSql = 'SELECT * FROM rainloop_pab_contacts '. 'WHERE id_user = :id_user'. ($this->bConsiderShare ? ' OR scope_type = :scope_type_share_all' : '') ; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT) ); if ($this->bConsiderShare) { $aParams[':scope_type_share_all'] = array(\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL, \PDO::PARAM_INT); } if (0 < \count($aSearchIds)) { $sSql .= ' AND id_contact IN ('.implode(',', $aSearchIds).')'; } $sSql .= ' ORDER BY display ASC LIMIT :limit OFFSET :offset'; $aParams[':limit'] = array($iLimit, \PDO::PARAM_INT); $aParams[':offset'] = array($iOffset, \PDO::PARAM_INT); $oStmt = $this->prepareAndExecute($sSql, $aParams); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); $aContacts = array(); $aIdContacts = array(); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { $iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0; if (0 < $iIdContact) { $aIdContacts[] = $iIdContact; $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); $oContact->IdContact = (string) $iIdContact; $oContact->IdContactStr = isset($aItem['id_contact_str']) ? (string) $aItem['id_contact_str'] : ''; $oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : ''; $oContact->ScopeType = isset($aItem['scope_type']) ? (int) $aItem['scope_type'] : \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_; $oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0; $oContact->ReadOnly = $iUserID !== (isset($aItem['id_user']) ? (int) $aItem['id_user'] : 0); $oContact->IdPropertyFromSearch = isset($aPropertyFromSearchIds[$iIdContact]) && 0 < $aPropertyFromSearchIds[$iIdContact] ? $aPropertyFromSearchIds[$iIdContact] : 0; $aContacts[$iIdContact] = $oContact; } } } unset($aFetch); if (0 < count($aIdContacts)) { $oStmt->closeCursor(); $sSql = 'SELECT * FROM rainloop_pab_properties WHERE id_contact IN ('.\implode(',', $aIdContacts).')'; $oStmt = $this->prepareAndExecute($sSql); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { if ($aItem && isset($aItem['id_prop'], $aItem['id_contact'], $aItem['prop_type'], $aItem['prop_value'])) { $iId = (int) $aItem['id_contact']; if (0 < $iId && isset($aContacts[$iId])) { $oProperty = new \RainLoop\Providers\PersonalAddressBook\Classes\Property(); $oProperty->IdProperty = (int) $aItem['id_prop']; $oProperty->ScopeType = isset($aItem['scope_type']) ? (int) $aItem['scope_type'] : \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_; $oProperty->Type = (int) $aItem['prop_type']; $oProperty->TypeCustom = isset($aItem['prop_type_custom']) ? (string) $aItem['prop_type_custom'] : ''; $oProperty->Value = (string) $aItem['prop_value']; $oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : ''; $oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0; $aContacts[$iId]->Properties[] = $oProperty; } } } } unset($aFetch); foreach ($aContacts as &$oItem) { $oItem->UpdateDependentValues(); } return \array_values($aContacts); } } } } return array(); } /** * @param string $sEmail * @param string $sID * @param bool $bIsStrID = false * * @return \RainLoop\Providers\PersonalAddressBook\Classes\Contact|null */ public function GetContactByID($sEmail, $mID, $bIsStrID = false) { $this->Sync(); $mID = \trim($mID); $iUserID = $this->getUserId($sEmail); $sSql = 'SELECT * FROM rainloop_pab_contacts '. 'WHERE ('. 'id_user = :id_user'. ($this->bConsiderShare ? ' OR scope_type = :scope_type_share_all' : ''). ')' ; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT) ); if ($this->bConsiderShare) { $aParams[':scope_type_share_all'] = array(\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL, \PDO::PARAM_INT); } if ($bIsStrID) { $sSql .= ' AND id_contact_str = :id_contact_str'; $aParams[':id_contact_str'] = array($mID, \PDO::PARAM_STR); } else { $sSql .= ' AND id_contact = :id_contact'; $aParams[':id_contact'] = array($mID, \PDO::PARAM_INT); } $sSql .= ' LIMIT 1'; $oContact = null; $iIdContact = 0; $oStmt = $this->prepareAndExecute($sSql, $aParams); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { $iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0; if (0 < $iIdContact) { $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); $oContact->IdContact = (string) $iIdContact; $oContact->IdContactStr = isset($aItem['id_contact_str']) ? (string) $aItem['id_contact_str'] : ''; $oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : ''; $oContact->ScopeType = isset($aItem['scope_type']) ? (int) $aItem['scope_type'] : \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_; $oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0; $oContact->ReadOnly = $iUserID !== (isset($aItem['id_user']) ? (int) $aItem['id_user'] : 0); $oContact->CardDavData = empty($aItem['carddav_data']) ? '' : (string) $aItem['carddav_data']; $oContact->CardDavHash = empty($aItem['carddav_hash']) ? \md5($oContact->CardDavData) : (string) $aItem['carddav_hash']; $oContact->CardDavSize = empty($aItem['carddav_size']) ? \strlen($oContact->CardDavData) : (int) $aItem['carddav_size']; } } } unset($aFetch); if (0 < $iIdContact && $oContact) { $oStmt->closeCursor(); $sSql = 'SELECT * FROM rainloop_pab_properties WHERE id_contact = '.$iIdContact; $oStmt = $this->prepareAndExecute($sSql); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { if ($aItem && isset($aItem['id_prop'], $aItem['id_contact'], $aItem['prop_type'], $aItem['prop_value'])) { if ((string) $oContact->IdContact === (string) $aItem['id_contact']) { $oProperty = new \RainLoop\Providers\PersonalAddressBook\Classes\Property(); $oProperty->IdProperty = (int) $aItem['id_prop']; $oProperty->ScopeType = isset($aItem['scope_type']) ? (int) $aItem['scope_type'] : \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_; $oProperty->Type = (int) $aItem['prop_type']; $oProperty->TypeCustom = isset($aItem['prop_type_custom']) ? (string) $aItem['prop_type_custom'] : ''; $oProperty->Value = (string) $aItem['prop_value']; $oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : ''; $oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0; $oContact->Properties[] = $oProperty; } } } } unset($aFetch); $oContact->UpdateDependentValues(); } } } return $oContact; } /** * @param string $sEmail * @param string $sSearch * @param int $iLimit = 20 * * @return array * * @throws \InvalidArgumentException */ public function GetSuggestions($sEmail, $sSearch, $iLimit = 20) { $sSearch = \trim($sSearch); if (0 === \strlen($sSearch)) { throw new \InvalidArgumentException('Empty Search argument'); } $this->Sync(); $iUserID = $this->getUserId($sEmail); $sTypes = implode(',', array( PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::FULLNAME )); $sSql = 'SELECT id_contact, id_prop, prop_type, prop_value FROM rainloop_pab_properties '. 'WHERE ('. 'id_user = :id_user'. ($this->bConsiderShare ? ' OR scope_type = :scope_type_share_all' : ''). ') AND prop_type IN ('.$sTypes.') AND prop_value LIKE :search ESCAPE \'=\''; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':limit' => array($iLimit, \PDO::PARAM_INT), ':search' => array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR) ); if ($this->bConsiderShare) { $aParams[':scope_type_share_all'] = array(\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL, \PDO::PARAM_INT); } $sSql .= ' ORDER BY prop_frec DESC'; $sSql .= ' LIMIT :limit'; $aResult = array(); $aFirstResult = array(); $aSkipIds = array(); $oStmt = $this->prepareAndExecute($sSql, $aParams); if ($oStmt) { $aIdContacts = array(); $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { $iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0; if (0 < $iIdContact) { $aIdContacts[$iIdContact] = $iIdContact; $iType = isset($aItem['prop_type']) ? (int) $aItem['prop_type'] : PropertyType::UNKNOWN; if (\in_array($iType, array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::FULLNAME))) { if (!\in_array($iIdContact, $aSkipIds)) { if (PropertyType::FULLNAME === $iType) { $aSkipIds[] = $iIdContact; } $aFirstResult[] = array( 'id_prop' => isset($aItem['id_prop']) ? (int) $aItem['id_prop'] : 0, 'id_contact' => $iIdContact, 'prop_value' => isset($aItem['prop_value']) ? (string) $aItem['prop_value'] : '', 'prop_type' => $iType ); } } } } } unset($aFetch); $aIdContacts = \array_values($aIdContacts); if (0 < count($aIdContacts)) { $oStmt->closeCursor(); $sTypes = \implode(',', array( PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::FULLNAME )); $sSql = 'SELECT id_prop, id_contact, prop_type, prop_value FROM rainloop_pab_properties '. 'WHERE prop_type IN ('.$sTypes.') AND id_contact IN ('.\implode(',', $aIdContacts).')'; $oStmt = $this->prepareAndExecute($sSql); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { $aNames = array(); $aEmails = array(); foreach ($aFetch as $aItem) { if ($aItem && isset($aItem['id_prop'], $aItem['id_contact'], $aItem['prop_type'], $aItem['prop_value'])) { $iIdContact = (int) $aItem['id_contact']; $iType = (int) $aItem['prop_type']; if (PropertyType::FULLNAME === $iType) { $aNames[$iIdContact] = $aItem['prop_value']; } else if (\in_array($iType, array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES))) { if (!isset($aEmails[$iIdContact])) { $aEmails[$iIdContact] = array(); } $aEmails[$iIdContact][] = $aItem['prop_value']; } } } foreach ($aFirstResult as $aItem) { if ($aItem && !empty($aItem['prop_value'])) { $iIdContact = (int) $aItem['id_contact']; $iType = (int) $aItem['prop_type']; if (PropertyType::FULLNAME === $iType) { if (isset($aEmails[$iIdContact]) && \is_array($aEmails[$iIdContact])) { foreach ($aEmails[$iIdContact] as $sEmail) { if (!empty($sEmail)) { $aResult[] = array($sEmail, (string) $aItem['prop_value']); } } } } else if (\in_array($iType, array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES))) { $aResult[] = array((string) $aItem['prop_value'], isset($aNames[$iIdContact]) ? (string) $aNames[$iIdContact] : ''); } } } } unset($aFetch); if ($iLimit < \count($aResult)) { $aResult = \array_slice($aResult, 0, $iLimit); } return $aResult; } } } return array(); } /** * @param string $sEmail * @param array $aEmails * @param bool $bCreateAuto = true * * @return bool */ public function IncFrec($sEmail, $aEmails, $bCreateAuto = true) { $self = $this; $aEmailsObjects = \array_map(function ($mItem) { $oResult = null; try { $oResult = \MailSo\Mime\Email::Parse(\trim($mItem)); } catch (\Exception $oException) {} return $oResult; }, $aEmails); if (0 === \count($aEmailsObjects)) { throw new \InvalidArgumentException('Empty Emails argument'); } $this->Sync(); $iUserID = $this->getUserId($sEmail); $sTypes = \implode(',', array( PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES )); $aExists = array(); $aEmailsToCreate = array(); $aEmailsToUpdate = array(); if ($bCreateAuto) { $sSql = 'SELECT prop_value FROM rainloop_pab_properties WHERE id_user = :id_user AND prop_type IN ('.$sTypes.')'; $oStmt = $this->prepareAndExecute($sSql, array( ':id_user' => array($iUserID, \PDO::PARAM_INT) )); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { if ($aItem && !empty($aItem['prop_value'])) { $aExists[] = \strtolower(\trim($aItem['prop_value'])); } } } } $aEmailsToCreate = \array_filter($aEmailsObjects, function ($oItem) use ($aExists, &$aEmailsToUpdate) { if ($oItem) { $sEmail = \strtolower(\trim($oItem->GetEmail())); if (0 < \strlen($sEmail)) { $aEmailsToUpdate[] = $sEmail; return !\in_array($sEmail, $aExists); } } return false; }); } else { foreach ($aEmailsObjects as $oItem) { if ($oItem) { $sEmail = \strtolower(\trim($oItem->GetEmail())); if (0 < \strlen($sEmail)) { $aEmailsToUpdate[] = $sEmail; } } } } unset($aEmails, $aEmailsObjects); if (0 < \count($aEmailsToCreate)) { $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); foreach ($aEmailsToCreate as $oEmail) { $oContact->ScopeType = \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_; if ('' !== \trim($oEmail->GetEmail())) { $oPropEmail = new \RainLoop\Providers\PersonalAddressBook\Classes\Property(); $oPropEmail->ScopeType = $oContact->ScopeType; $oPropEmail->Type = \RainLoop\Providers\PersonalAddressBook\Enumerations\PropertyType::EMAIl_PERSONAL; $oPropEmail->Value = \strtolower(\trim($oEmail->GetEmail())); $oContact->Properties[] = $oPropEmail; } if ('' !== \trim($oEmail->GetDisplayName())) { $oPropName = new \RainLoop\Providers\PersonalAddressBook\Classes\Property(); $oPropName->ScopeType = $oContact->ScopeType; $oPropName->Type = \RainLoop\Providers\PersonalAddressBook\Enumerations\PropertyType::FULLNAME; $oPropName->Value = \trim($oEmail->GetDisplayName()); $oContact->Properties[] = $oPropName; } if (0 < \count($oContact->Properties)) { $this->ContactSave($sEmail, $oContact); } $oContact->Clear(); } } $sSql = 'UPDATE rainloop_pab_properties SET prop_frec = prop_frec + 1 WHERE id_user = :id_user AND prop_type IN ('.$sTypes; $aEmailsQuoted = \array_map(function ($mItem) use ($self) { return $self->quoteValue($mItem); }, $aEmailsToUpdate); if (1 === \count($aEmailsQuoted)) { $sSql .= ') AND prop_value = '.$aEmailsQuoted[0]; } else { $sSql .= ') AND prop_value IN ('.\implode(',', $aEmailsQuoted).')'; } return !!$this->prepareAndExecute($sSql, array( ':id_user' => array($iUserID, \PDO::PARAM_INT), )); } /** * @return string */ public function Test() { $sResult = ''; try { $this->Sync(); if (0 >= $this->getVersion($this->sDsnType.'-pab-version')) { $sResult = 'Unknown database error'; } } catch (\Exception $oException) { $sResult = $oException->getMessage(); if (!empty($sResult) && !\MailSo\Base\Utils::IsAscii($sResult) && !\MailSo\Base\Utils::IsUtf8($sResult)) { $sResult = @\utf8_encode($sResult); } if (!\is_string($sResult) || empty($sResult)) { $sResult = 'Unknown database error'; } } return $sResult; } private function getInitialTablesArray($sDbType) { $sInitial = ''; $aResult = array(); switch ($sDbType) { case 'mysql': $sInitial = <<sDsnType) { case 'mysql': return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array( 1 => $this->getInitialTablesArray($this->sDsnType), 2 => array( 'ALTER TABLE rainloop_pab_contacts ADD id_contact_str varchar(128) NOT NULL DEFAULT \'\' AFTER id_contact;', 'ALTER TABLE rainloop_pab_contacts ADD carddav_data MEDIUMTEXT;', 'ALTER TABLE rainloop_pab_contacts ADD carddav_hash varchar(128) NOT NULL DEFAULT \'\';', 'ALTER TABLE rainloop_pab_contacts ADD carddav_size int UNSIGNED NOT NULL DEFAULT 0;', 'CREATE TABLE IF NOT EXISTS rainloop_pab_users ( id_user int UNSIGNED NOT NULL, email varchar(128) NOT NULL, pass_hash varchar(128) NOT NULL )/*!40000 ENGINE=INNODB */;' ) )); case 'pgsql': return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array( 1 => $this->getInitialTablesArray($this->sDsnType), 2 => array( 'ALTER TABLE rainloop_pab_contacts ADD id_contact_str varchar(128) NOT NULL DEFAULT \'\';', 'ALTER TABLE rainloop_pab_contacts ADD carddav_data TEXT;', 'ALTER TABLE rainloop_pab_contacts ADD carddav_hash varchar(128) NOT NULL DEFAULT \'\';', 'ALTER TABLE rainloop_pab_contacts ADD carddav_size integer NOT NULL DEFAULT 0;', 'CREATE TABLE rainloop_pab_users ( id_user integer NOT NULL, email varchar(128) NOT NULL, pass_hash varchar(128) NOT NULL );' ) )); case 'sqlite': return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array( 1 => $this->getInitialTablesArray($this->sDsnType), 2 => array( 'ALTER TABLE rainloop_pab_contacts ADD id_contact_str text NOT NULL DEFAULT \'\';', 'ALTER TABLE rainloop_pab_contacts ADD carddav_data text;', 'ALTER TABLE rainloop_pab_contacts ADD carddav_hash text NOT NULL DEFAULT \'\';', 'ALTER TABLE rainloop_pab_contacts ADD carddav_size integer NOT NULL DEFAULT 0;', 'CREATE TABLE rainloop_pab_users ( id_user integer NOT NULL, email text NOT NULL, pass_hash text NOT NULL );' ) )); } return false; } /** * @param int $iUserID * @param int $iIdContact * @return array */ private function getContactFreq($iUserID, $iIdContact) { $aResult = array(); $sTypes = \implode(',', array( PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES )); $sSql = 'SELECT prop_value, prop_frec FROM rainloop_pab_properties WHERE id_user = :id_user AND id_contact = :id_contact AND prop_type IN ('.$sTypes.')'; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':id_contact' => array($iIdContact, \PDO::PARAM_INT) ); $oStmt = $this->prepareAndExecute($sSql, $aParams); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch)) { foreach ($aFetch as $aItem) { if ($aItem && !empty($aItem['prop_value']) && !empty($aItem['prop_frec'])) { $aResult[$aItem['prop_value']] = (int) $aItem['prop_frec']; } } } } return $aResult; } /** * @param string $sSearch * @param string $sEscapeSign = '=' * * @return string */ private function specialConvertSearchValue($sSearch, $sEscapeSign = '=') { return '%'.\str_replace(array($sEscapeSign, '_', '%'), array($sEscapeSign.$sEscapeSign, $sEscapeSign.'_', $sEscapeSign.'%'), $sSearch).'%'; } /** * @param string $sSearch * * @return string */ private function specialConvertSearchValueCustomPhone($sSearch) { return '%'.\preg_replace('/[^\d]/', '', $sSearch).'%'; } /** * @return array */ protected function getPdoAccessData() { return array($this->sDsnType, $this->sDsn, $this->sUser, $this->sPassword); } }