diff --git a/Superfecta.class.php b/Superfecta.class.php
index a4adc425..49c8e139 100644
--- a/Superfecta.class.php
+++ b/Superfecta.class.php
@@ -39,6 +39,52 @@ function __construct($options=array()) {
private $destination = null;
private $agi = null;
+ /* -------------------- Added: safe logging controls (non-breaking) -------------------- */
+ /** @var int $logLevel Current log threshold. 1=default (all existing out() calls map here) */
+ private $logLevel = 1;
+
+ /** @var bool $suppressWebEcho When true, out() won’t echo to web (keeps AGI verbose only) */
+ private $suppressWebEcho = false;
+
+ /**
+ * Change log level (for future use). Existing out() calls use level=1,
+ * so behavior remains identical unless you raise this threshold.
+ */
+ public function setLogLevel($level) {
+ $lvl = intval($level);
+ if ($lvl < 0) { $lvl = 0; }
+ $this->logLevel = $lvl;
+ }
+
+ /** Suppress web echo (still logs via AGI->verbose when available). */
+ public function setSuppressWebEcho($bool) {
+ $this->suppressWebEcho = (bool)$bool;
+ }
+
+ /**
+ * Safe logger used by out(). Honors logLevel and suppressWebEcho.
+ * Non-breaking: if you never call setLogLevel/setSuppressWebEcho,
+ * behavior is identical to the original out().
+ */
+ private function safeLog($message, $level = 1) {
+ if ($level > $this->logLevel) {
+ return;
+ }
+ // Prefer AGI verbose when available (typical at call time)
+ if (is_object($this->agi)) {
+ $this->agi->verbose($message);
+ return;
+ }
+ // Otherwise echo to web/CLI unless suppressed for web
+ if (php_sapi_name() != "cli") {
+ if ($this->suppressWebEcho) { return; }
+ echo "".$message."
";
+ } else {
+ echo $message."\n";
+ }
+ }
+ /* ------------------ End: safe logging controls (non-breaking) ------------------ */
+
public function install() {
$sql = "SELECT * FROM superfectaconfig LIMIT 1;";
$res = $this->Database->query($sql);
@@ -83,14 +129,9 @@ public function chownFreepbx() {
];
}
+ /** Non-breaking: route legacy out() via safeLog(level=1) */
private function out($message) {
- if(is_object($this->agi)) {
- $this->agi->verbose($message);
- } elseif (php_sapi_name() != "cli") {
- echo "".$message."
";
- } else {
- echo $message."\n";
- }
+ $this->safeLog($message, 1);
}
public function setAgi($agi) {
@@ -135,7 +176,7 @@ public function execute($scheme='ALL', $request=[], $debug=0, $keepGoing=false)
);
foreach ($schemes as $s) {
- try{
+ try{
$this->out("");
$this->out(sprintf(_("Starting scheme %s"),$s['name']));
//reset these each time
@@ -160,7 +201,7 @@ public function execute($scheme='ALL', $request=[], $debug=0, $keepGoing=false)
$superfecta = NEW \superfecta_single($options);
break;
}
-
+
$superfecta->setDebug($debug);
$superfecta->setCLI(true);
$superfecta->setDID($did);
@@ -174,7 +215,7 @@ public function execute($scheme='ALL', $request=[], $debug=0, $keepGoing=false)
$this->out(_("No matching DID rules. Skipping scheme"));
continue;
}
-
+
// Determine if the CID matches any patterns defined for this scheme
$rule_match = $superfecta->match_pattern_all((isset($options['scheme_settings']['CID_rules'])) ? $options['scheme_settings']['CID_rules'] : '', $cnum);
if ($rule_match['number']) {
@@ -222,39 +263,36 @@ public function execute($scheme='ALL', $request=[], $debug=0, $keepGoing=false)
if (!empty($callerid)) {
$found = true;
- $this->out(sprintf(_("Caller ID before strip_tags: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID before strip_tags: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
$callerid = trim(strip_tags($callerid));
- $this->out(sprintf(_("Caller ID after strip_tags: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
- $stripAccentsCharacters = (isset($options['scheme_settings']['Strip_Accent_Characters'])) ? $options['scheme_settings']['Strip_Accent_Characters'] : 'Y'; // Default to stripping accent character for backward compatibility
- $this->out("Strip_Accent_Characters: " . $stripAccentsCharacters);
+ // $this->out(sprintf(_("Caller ID after strip_tags: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ $stripAccentsCharacters = (isset($options['scheme_settings']['Strip_Accent_Characters'])) ? $options['scheme_settings']['Strip_Accent_Characters'] : 'Y';
+ // $this->out("Strip_Accent_Characters: " . $stripAccentsCharacters);
if ($superfecta->isCharSetIA5() && $stripAccentsCharacters == 'Y') {
- $this->out(sprintf(_("Caller ID before stripAccents: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID before stripAccents: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
$callerid = $superfecta->stripAccents($callerid);
- $this->out(sprintf(_("Caller ID after stripAccents: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID after stripAccents: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
}
- //Why?
- $this->out(sprintf(_("Caller ID before preg_replace: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID before preg_replace: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
$callerid = preg_replace("/[\";']/", "", $callerid);
- $this->out(sprintf(_("Caller ID after preg_replace: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
-
- // Display issues on phones and CDR with special characters
- // convert CNAM to UTF-8 to fix
+ // $this->out(sprintf(_("Caller ID after preg_replace: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+
$character_encodings = (isset($options['scheme_settings']['Character_Encodings'])) ? $options['scheme_settings']['Character_Encodings'] : self::DEFAULT_CHARACTER_ENCODINGS;
- $this->out(sprintf(_("Character Encodings: '%s'"), $character_encodings));
+ // $this->out(sprintf(_("Character Encodings: '%s'"), $character_encodings));
if (in_array('pass',explode(',', $character_encodings))){
- $this->out(_("Bypassing character conversion."));
+ $this->out(_("Bypassing character conversion."));
}
elseif(!function_exists('mb_convert_encoding')) {
$this->out(_("Function mb_convert_encoding does not exist."));
}
else{
- $this->out(_("Converting result to UTF-8"));
+ // $this->out(_("Converting result to UTF-8"));
try{
- $this->out(sprintf(_("Caller ID before mb_convert_encoding: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID before mb_convert_encoding: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
$callerid = mb_convert_encoding($callerid, "UTF-8", $character_encodings);
- $this->out(sprintf(_("Caller ID after mb_convert_encoding: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID after mb_convert_encoding: %s, length: %s"), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
}
catch(Exception $e) {
$this->out(sprintf(_('Caught exception calling mb_convert_encoding: %s'), $e->getMessage()));
@@ -262,11 +300,11 @@ public function execute($scheme='ALL', $request=[], $debug=0, $keepGoing=false)
}
- $this->out(sprintf(_("Caller_Id_Max_Length: '%s'"),$Caller_Id_Max_Length));
+ // $this->out(sprintf(_("Caller_Id_Max_Length: '%s'"),$Caller_Id_Max_Length));
if ($Caller_Id_Max_Length != -1){
- $this->out(sprintf(_("Caller ID before %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID before %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
$callerid = (function_exists('mb_substr')) ? mb_substr($callerid, 0, $Caller_Id_Max_Length) : substr($callerid, 0, $Caller_Id_Max_Length);
- $this->out(sprintf(_("Caller ID after %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID after %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
}
else{
$this->out(sprintf(_("Caller ID string length is not limited")));
@@ -285,9 +323,9 @@ public function execute($scheme='ALL', $request=[], $debug=0, $keepGoing=false)
}
if ($Caller_Id_Max_Length != -1){
- $this->out(sprintf(_("Caller ID before %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID before %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
$callerid = (function_exists('mb_substr')) ? mb_substr($callerid, 0, $Caller_Id_Max_Length) : substr($callerid, 0, $Caller_Id_Max_Length);
- $this->out(sprintf(_("Caller ID after %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
+ // $this->out(sprintf(_("Caller ID after %s: %s, length: %s"), ((function_exists('mb_substr')) ? 'mb_substr' : 'substr'), $callerid,strlen($callerid)) . ((function_exists('mb_strlen')) ? (sprintf(_(", mb_strlen: %s"),mb_strlen($callerid))) : ''));
}
//Set Spam Destination
@@ -319,7 +357,7 @@ public function execute($scheme='ALL', $request=[], $debug=0, $keepGoing=false)
$this->out(sprintf(_('Caught exception: %s
Skipping scheme %s
'), $e->getMessage(), $s['name']));
}
}
-
+
if(empty($callerid) && !$keepGoing) {
//No callerid so I guess?
return $trunk_info['calleridname'];
@@ -390,7 +428,7 @@ public function ajaxCustomHandler() {
$schem = htmlEntities($_REQUEST['scheme']);
$thedid = htmlEntities($_REQUEST['thedid']);
if (empty($thedid)){
- $thedid = '5555555555';
+ $thedid = '5555555555';
}
echo ""._('Debug is on and set at level:')." ". $level."";
echo ""._('The DID:')." ".$thedid."";
@@ -868,4 +906,4 @@ public function didList($id = false){
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return is_array($results) ? $results : array();
}
-}
+}
\ No newline at end of file
diff --git a/includes/oauth-google/Google_Service_ReadContacts.php b/includes/oauth-google/Google_Service_ReadContacts.php
index 416bad44..c66d3328 100644
--- a/includes/oauth-google/Google_Service_ReadContacts.php
+++ b/includes/oauth-google/Google_Service_ReadContacts.php
@@ -2,25 +2,23 @@
/*
* Copyright 2010 Google Inc.
*
-* Licensed under the Apache License, Version 2.0 ( the "License" );
-you may not
-* use this file except in compliance with the License. You may obtain a copy of
-* the License at
+* Licensed under the Apache License, Version 2.0 (the "License" );
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-* License for the specific language governing permissions and limitations under
-* the License.
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
*/
-// based on original work from the PHP Laravel framework
-if ( !function_exists( 'str_contains' ) ) {
- function str_contains( $haystack, $needle ) {
- return $needle !== '' && mb_strpos( $haystack, $needle ) !== false;
- }
+if (!function_exists('str_contains')) {
+ function str_contains($haystack, $needle) {
+ return $needle !== '' && mb_strpos($haystack, $needle) !== false;
+ }
}
require_once 'Google/Service.php';
@@ -28,255 +26,265 @@ function str_contains( $haystack, $needle ) {
#[AllowDynamicProperties]
class Google_Service_ReadContacts {
- const SCOPE_CONTACTS_READONLY = "https://www.googleapis.com/auth/contacts.readonly";
- const BASE_URL = "https://people.googleapis.com/v1/people:searchContacts";
-
- private $query;
- private $gam;
-
- /**
- * Constructs the internal representation of the Admin service.
- *
- * @param Google_Client $client
- */
-
- public function __construct( GoogleAuthManager $authManager ) {
- $this->gam = $authManager;
- }
-
- public function setAccessToken( $at ) {
- $this->access_token = $at;
- }
-
- public function getContactsForNumberStarting( $query ) {
- $appendPhoneTypes = true;
- $useNickNames = true;
- $displayLastNameFirstName = false;
- try {
- // echo '
in getContactsForNumberStarting: '.json_encode( $this ).'
';
- $append_phone_types = $this->gam->append_phone_types;
- $use_nicknames = $this->gam->use_nicknames;
-
- if ( $append_phone_types === 'on' ) {
- $appendPhoneTypes = true;
- } else {
- $appendPhoneTypes = false;
- }
- if ( $this->gam->use_nicknames === 'on' ) {
- $useNickNames = true;
- } else {
- $useNickNames = false;
- }
- } catch( Exception $e ) {
- //echo 'Unable to get phone Type Flag: '.$e->getMessage().'
';
- }
+ // Expected by Superfecta
+ const SCOPE_CONTACTS = "https://www.googleapis.com/auth/contacts";
+ const SCOPE_CONTACTS_READONLY = "https://www.googleapis.com/auth/contacts.readonly";
- try {
- if ( $this->gam->display_lastname_firstname === 'on' ) {
- $displayLastNameFirstName = true;
- } else {
- $displayLastNameFirstName = false;
- }
- } catch( Exception $e ) {
- echo 'Unable to get Last Name First Name Flag Flag: '.$e->getMessage().'
';
- }
+ const BASE_URL = "https://people.googleapis.com/v1/people:searchContacts";
- if ( $appendPhoneTypes ) {
- echo 'Appending Phone Types to names.
';
- } else {
- echo 'Not Appending Phone Types to names.
';
- }
+ private $query;
+ private $gam;
+ private $access_token;
- if ( $useNickNames ) {
- echo 'Using Nicknames when available.
';
- } else {
- echo 'Not using Nicknames.
';
- }
- if ( $displayLastNameFirstName ) {
- echo 'Displaying Last Name, First Name format
';
- } else {
- echo 'Displaying Default Name format.
';
+ // Behavior flags
+ private $stopAfterFirstMatch = false;
+
+ // Debug control
+ private $debugEnabled = true;
+ private $last_http_code = null;
+
+ public function __construct(GoogleAuthManager $authManager) {
+ $this->gam = $authManager;
+
+ try {
+ if (isset($this->gam->debug_level)) {
+ $lvl = intval($this->gam->debug_level);
+ $this->debugEnabled = ($lvl > 0);
+ } elseif (isset($this->gam->debug)) {
+ $this->debugEnabled = ($this->gam->debug === 'on' || $this->gam->debug === 1 || intval($this->gam->debug) > 0);
+ }
+ } catch (\Throwable $e) {}
+
+ try {
+ if (isset($this->gam->stop_after_first_match) && $this->gam->stop_after_first_match === 'on') {
+ $this->stopAfterFirstMatch = true;
+ }
+ } catch (\Throwable $e) {}
}
- $counter = 0;
- $output = array();
-
- $this->setAccessToken( $this->gam->getAccessToken() );
- $query_number = $this->cleanNumber( $query );
- $len_query = strlen( $query_number );
- $this->query = $query_number;
-
- $result = $this->curl_file_get_contents( $this->constructFinalUrl() );
- //echo 'result:
'.$result.'
';
- $result_json = json_decode( $result );
- dbug( $result_json );
-
- if ( isset( $result_json->error ) ) {
- echo 'Error getting result:
'.$result.'
';
- $results = array( 'success' => 'no', 'data' => json_encode( $result_json->error ) );
- return $results;
+
+ public function setAccessToken($at) {
+ $this->access_token = $at;
}
- try {
- $count = 0;
- if ( isset( $result_json->results ) ) {
- foreach ( $result_json->results as $entry ) {
- $count++;
- $name = "";
- try {
- if ( $useNickNames and isset( $entry->person->nicknames ) ) {
- $name = $entry->person->nicknames[0]->value;
- dbug( $name );
- echo '
Nickname: '.$name;
- } elseif ( isset( $entry->person->names ) ) {
- if ($displayLastNameFirstName){
- $name = $entry->person->names[0]->displayNameLastFirst;
- }
- else{
- $name = $entry->person->names[0]->displayName;
- }
- dbug( $name );
- echo '
Name: '.$name;
- } elseif ( isset( $entry->person->organizations ) ) {
- $name = $entry->person->organizations[0]->name;
- dbug( $name );
- echo '
Organization: '.$name;
- }
- echo '
';
-
- if ( !empty( $name ) ) {
- if ( isset( $entry->person->phoneNumbers ) ) {
- foreach ( $entry->person->phoneNumbers as $phoneNumber ) {
- $nameWithType = $name;
- try {
- if ( $appendPhoneTypes and isset( $phoneNumber->formattedType ) ) {
- $nameWithType = $name.' ('.$phoneNumber->formattedType.')';
- }
- } catch( Exception $e ) {
- echo 'Unable to get phone number type for '.$name.': '.$e->getMessage().'
';
- }
-
- try {
- if ( isset( $phoneNumber->canonicalForm ) ) {
- $no = $phoneNumber->canonicalForm;
- echo $nameWithType.', Canonical Form: '.$no.'
';
- $score = $this->subStringScore( $no, $query_number );
- if ( $score > 0 ) {
- $output[$counter] = $option = array( 'name' => $nameWithType, 'number' => $no, 'score' => $score );
- $counter++;
- echo $counter.'. '.$nameWithType.', '.$no.', Score:'.$score.'
';
- }
+ public function getContactsForNumberStarting($query) {
+ $appendPhoneTypes = true;
+ $useNickNames = true;
+ $displayLastNameFirstName = false;
+
+ try { $appendPhoneTypes = ($this->gam->append_phone_types === 'on'); } catch (\Throwable $e) {}
+ try { $useNickNames = ($this->gam->use_nicknames === 'on'); } catch (\Throwable $e) {}
+ try { $displayLastNameFirstName = ($this->gam->display_lastname_firstname === 'on'); } catch (\Throwable $e) {}
+
+ $this->debugEcho($appendPhoneTypes ? "Appending Phone Types to names.
" : "Not Appending Phone Types to names.
");
+ $this->debugEcho($useNickNames ? "Using Nicknames when available.
" : "Not using Nicknames.
");
+ $this->debugEcho($displayLastNameFirstName ? "Displaying Last Name, First Name format
" : "Displaying Default Name format.
");
+
+ $this->setAccessToken($this->gam->getAccessToken());
+
+ // 1) E.164 first
+ $queryE164 = $this->normalizeToE164($query);
+ $this->debugEcho("Normalized query number (E.164): {$queryE164}
");
+ $result_json = $this->performQueryAndDecode($queryE164);
+
+ // 2) NANP format
+ if (!$this->hasResults($result_json)) {
+ $this->debugEcho("No results with E.164, retrying with NANP format
");
+ $queryNANP = $this->formatNANP($query);
+ $result_json = $this->performQueryAndDecode($queryNANP);
+ }
+
+ // 3) Raw digits
+ if (!$this->hasResults($result_json)) {
+ $this->debugEcho("No results with NANP, retrying with raw digits
");
+ $queryDigits = preg_replace('/\D+/', '', $query);
+ $result_json = $this->performQueryAndDecode($queryDigits);
+ }
+
+ if (isset($result_json->error)) {
+ $this->debugEcho('Error getting result:
' . htmlspecialchars(json_encode($result_json->error)) . '
');
+ return ['success' => 'no', 'data' => json_encode($result_json->error)];
+ }
+
+ $output = [];
+ $counter = 0;
+
+ if (isset($result_json->results)) {
+ foreach ($result_json->results as $entry) {
+ try {
+ $name = "";
+ if ($useNickNames && isset($entry->person->nicknames)) {
+ $name = $entry->person->nicknames[0]->value;
+ $this->debugEcho("
Nickname: " . htmlspecialchars($name));
+ } elseif (isset($entry->person->names)) {
+ $name = $displayLastNameFirstName ?
+ $entry->person->names[0]->displayNameLastFirst :
+ $entry->person->names[0]->displayName;
+ $this->debugEcho("
Name: " . htmlspecialchars($name));
+ } elseif (isset($entry->person->organizations)) {
+ $name = $entry->person->organizations[0]->name;
+ $this->debugEcho("
Organization: " . htmlspecialchars($name));
}
- } catch( Exception $e ) {
- echo 'Error Parsing Canonical phone number for '.$nameWithType.': ' .$e->getMessage().'
';
- }
-
- try {
- if ( isset( $phoneNumber->value ) ) {
- $no = $this->cleanNumber( $phoneNumber->value );
- echo $nameWithType.', Value: '.$no.'
';
- $score = $this->subStringScore( $no, $query_number );
- if ( $score > 0 ) {
- $counter++;
- $output[$counter] = $option = array( 'name' => $nameWithType, 'number' => $no, 'score' => $score );
- echo $counter.'. '.$nameWithType.', '.$no.', Score:'.$score.'
';
- }
+
+ if (!empty($name) && isset($entry->person->phoneNumbers)) {
+ foreach ($entry->person->phoneNumbers as $phoneNumber) {
+ $nameWithType = $name;
+ if ($appendPhoneTypes && isset($phoneNumber->formattedType)) {
+ $nameWithType = $name . ' (' . $phoneNumber->formattedType . ')';
+ }
+
+ if (isset($phoneNumber->canonicalForm)) {
+ $no = $phoneNumber->canonicalForm;
+ $this->debugEcho($this->h("$nameWithType, Canonical Form: $no") . "
");
+ $score = $this->subStringScore($no, $queryE164);
+ if ($score > 0) {
+ $output[$counter] = ['name' => $nameWithType, 'number' => $no, 'score' => $score];
+ $counter++;
+ $this->debugEcho($this->h("$counter. $nameWithType, $no, Score:$score") . "
");
+ if ($this->stopAfterFirstMatch) {
+ $this->debugEcho("Stopping after first match (flag enabled)
");
+ return ['success' => 'yes', 'data' => $output];
+ }
+ }
+ }
+
+ if (isset($phoneNumber->value)) {
+ $noRaw = $this->cleanNumber($phoneNumber->value);
+ $this->debugEcho($this->h("$nameWithType, Value: $noRaw") . "
");
+ $score = $this->subStringScore($noRaw, $queryE164);
+ if ($score > 0) {
+ $output[$counter] = ['name' => $nameWithType, 'number' => $noRaw, 'score' => $score];
+ $counter++;
+ $this->debugEcho($this->h("$counter. $nameWithType, $noRaw, Score:$score") . "
");
+ if ($this->stopAfterFirstMatch) {
+ $this->debugEcho("Stopping after first match (flag enabled)
");
+ return ['success' => 'yes', 'data' => $output];
+ }
+ }
+ }
+ }
}
- } catch( Exception $e ) {
- echo 'Error Parsing phone number for '.$nameWithType.': ' .$e->getMessage().'
';
- }
+ } catch (\Throwable $e) {
+ $this->debugEcho('Message: ' . $this->h($e->getMessage()) . '
');
}
- }
}
- } catch( Exception $e ) {
- echo 'Message 2: ' .$e->getMessage().'
';
- }
}
- }
- } catch( Exception $e ) {
- echo 'Message 3: ' .$e->getMessage().'
';
+
+ $this->debugEcho('Found ' . $counter . ' matches
');
+ return ['success' => 'yes', 'data' => $output];
}
- echo 'found '.$counter.' matches
';
- $results = array( 'success' => 'yes', 'data' => $output );
- if ( sizeof( $results['data'] ) > 0 ) {
- return $results;
+ /* -------------------- Helpers -------------------- */
+
+ private function performQueryAndDecode($queryString) {
+ $this->query = $queryString;
+ $url = $this->constructFinalUrl();
+ $this->debugEcho('URL: ' . $this->h($url) . '
');
+
+ $result = $this->curl_file_get_contents($url);
+
+ $this->debugEcho("cURL HTTP Code: " . $this->last_http_code . "
");
+ $this->debugEcho("Raw API result:
" . $this->prettyOrRaw($result) . "
");
+
+ $decoded = json_decode($result);
+ return $decoded ?: (object)[];
}
- if ( substr( $query, 0, 1 ) == '+' ) {
- // No matches
- return $results;
+ private function hasResults($result_json) {
+ return (isset($result_json->results) && !empty($result_json->results));
}
- // For US Numbers try to prefix number with 1
- if ( substr( $query, 0, 1 ) != '1' ) {
- $query1 = '1'.$query;
- echo '
Searching Google Contacts for number with a 1 appended: '.$query1.'
';
- $results = $this->getContactsForNumberStarting( $query1 );
- if ( sizeof( $results['data'] ) > 0 ) {
- //we have some matches
- return $results;
- }
- // We didn't find a match with a number starting with 1
+ private function normalizeToE164($number) {
+ // Always strip all non-digits except leading '+'
+ $clean = preg_replace('/[^\d+]/', '', $number);
+
+ if (substr($clean, 0, 1) === '+') {
+ return $clean;
+ }
+ if (strlen($clean) === 10) {
+ return '+1' . $clean;
+ } elseif (strlen($clean) === 11 && substr($clean, 0, 1) === '1') {
+ return '+' . $clean;
+ }
+ return '+' . $clean;
}
- // Try to prefix + sign
- $query1 = '+'.$query;
- echo '
Searching Google Contacts for number with a + sign appended: '.$query1.'
';
- $results = $this->getContactsForNumberStarting($query1);
- //echo sizeof($results['data']).'
';
- if (sizeof($results['data']) > 0){
- //we have some matches
- return $results;
+ private function formatNANP($number) {
+ $digits = preg_replace('/\D+/', '', $number);
+ if (strlen($digits) == 10) {
+ return '(' . substr($digits, 0, 3) . ') ' . substr($digits, 3, 3) . '-' . substr($digits, 6);
+ } elseif (strlen($digits) == 11 && substr($digits, 0, 1) == '1') {
+ return '(' . substr($digits, 1, 3) . ') ' . substr($digits, 4, 3) . '-' . substr($digits, 7);
+ }
+ return $number;
}
- // No Matches
- return $results;
- }
-
- private function cleanNumber($number) {
- $result = preg_replace('/[^0-9+]*/', '', $number);
- return $result;
- }
-
- private function subStringScore($number, $prefix) {
- if (str_contains($number, $prefix)){
- $result = strlen($prefix);
+
+ private function cleanNumber($number) {
+ return preg_replace('/[^0-9+]*/', '', $number);
}
- elseif (str_contains($prefix, $number)){
- $result = strlen($number);
+
+ private function subStringScore($number, $prefix) {
+ if (str_contains($number, $prefix)) return strlen($prefix);
+ if (str_contains($prefix, $number)) return strlen($number);
+ return 0;
}
- else{
- $result = 0;
+
+ private function constructFinalUrl() {
+ $url = self::BASE_URL;
+ $url .= '?readMask=names,nicknames,organizations,phoneNumbers';
+ $url .= '&access_token=' . $this->access_token;
+ if (isset($this->query)) $url .= '&query=' . urlencode($this->query);
+ return $url;
}
- return $result;
- }
-
- private function constructFinalUrl() {
- $result = Google_Service_ReadContacts::BASE_URL;
- $result .= '?readMask=names,nicknames,organizations,phoneNumbers';
- $result .= '&access_token='.$this->access_token;
- if (isset($this->query)) $result .= '&query='.$this->query;
- dbug($result);
- echo 'url: '.$result.'
';
- return $result;
- }
-
- private function curl_file_get_contents($url) {
- $curl = curl_init();
- $userAgent = 'Mozilla/4.0 ( compatible;MSIE 6.0;Windows NT 5.1;.NET CLR 1.1.4322 )';
-
- curl_setopt($curl, CURLOPT_URL, $url); //The URL to fetch. This can also be set when initializing a session with curl_init().
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); //TRUE to return the transfer as a string of the return value of curl_exec() instead of outputting it out directly.
- curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); //The number of seconds to wait while trying to connect.
-
- curl_setopt($curl, CURLOPT_USERAGENT, $userAgent); //The contents of the "User-Agent: " header to be used in a HTTP request.
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE); //To follow any "Location: " header that the server sends as part of the HTTP header.
- curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE); //To automatically set the Referer: field in requests where it follows a Location: redirect.
- curl_setopt($curl, CURLOPT_TIMEOUT, 10); //The maximum number of seconds to allow cURL functions to execute.
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); //To stop cURL from verifying the peer's certificate.
- curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST, 0 );
-
- $contents = curl_exec( $curl );
- curl_close( $curl );
- return $contents;
+
+ private function curl_file_get_contents($url) {
+ $curl = curl_init();
+ $userAgent = 'Mozilla/5.0 (Superfecta GoogleContacts)';
+
+ curl_setopt($curl, CURLOPT_URL, $url);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 10);
+ curl_setopt($curl, CURLOPT_USERAGENT, $userAgent);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_AUTOREFERER, true);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
+ curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+
+ $contents = curl_exec($curl);
+ $this->last_http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+
+ if ($contents === false) {
+ $err = curl_error($curl);
+ $this->debugEcho('cURL Error: ' . $this->h($err) . '
');
+ }
+
+ curl_close($curl);
+ return $contents ?: '';
}
- }
+
+ private function prettyOrRaw($result) {
+ if (!$this->debugEnabled) return '';
+ $decodedAssoc = json_decode($result, true);
+ if (json_last_error() === JSON_ERROR_NONE) {
+ $pretty = json_encode($decodedAssoc, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+ $pretty = preg_replace_callback('/^( +)/m', function($m) {
+ $spaces = strlen($m[1]);
+ $groups = intdiv($spaces, 4);
+ return str_repeat(' ', max(1, $groups));
+ }, $pretty);
+ return '
' . htmlspecialchars($pretty) . ''; + } + return '
' . htmlspecialchars($result) . ''; + } + + private function debugEcho($msg) { + if ($this->debugEnabled) { + echo $msg; + } + } + + private function h($s) { + return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + } +}