2017-11-15 15:47:28 +01:00
< ? php
2018-01-25 03:08:45 +01:00
2017-11-15 15:47:28 +01:00
/**
* @ file src / Model / GlobalContact . php
* @ brief This file includes the GlobalContact class with directory related functions
*/
namespace Friendica\Model ;
2018-07-20 04:15:21 +02:00
use Exception ;
2018-07-20 08:04:23 +02:00
use Friendica\Core\Config ;
2018-10-29 22:20:46 +01:00
use Friendica\Core\Logger ;
2018-08-11 22:40:44 +02:00
use Friendica\Core\Protocol ;
2017-11-15 15:47:28 +01:00
use Friendica\Core\System ;
2017-11-15 17:51:29 +01:00
use Friendica\Core\Worker ;
2018-07-20 14:19:26 +02:00
use Friendica\Database\DBA ;
2017-12-07 15:05:35 +01:00
use Friendica\Network\Probe ;
2017-11-15 15:47:28 +01:00
use Friendica\Protocol\PortableContact ;
2018-01-27 03:38:34 +01:00
use Friendica\Util\DateTimeFormat ;
2018-01-27 05:09:48 +01:00
use Friendica\Util\Network ;
2018-11-08 17:28:29 +01:00
use Friendica\Util\Strings ;
2017-11-15 15:47:28 +01:00
/**
* @ brief This class handles GlobalContact related functions
*/
2017-12-07 15:09:28 +01:00
class GContact
2017-11-15 15:47:28 +01:00
{
/**
* @ brief Search global contact table by nick or name
*
* @ param string $search Name or nick
* @ param string $mode Search mode ( e . g . " community " )
*
* @ return array with search results
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2017-11-15 15:47:28 +01:00
*/
public static function searchByName ( $search , $mode = '' )
{
2018-03-17 08:50:49 +01:00
if ( empty ( $search )) {
return [];
}
2017-11-15 15:47:28 +01:00
2018-03-17 08:50:49 +01:00
// check supported networks
if ( Config :: get ( 'system' , 'diaspora_enabled' )) {
2018-08-11 22:40:44 +02:00
$diaspora = Protocol :: DIASPORA ;
2018-03-17 08:50:49 +01:00
} else {
2018-08-11 22:40:44 +02:00
$diaspora = Protocol :: DFRN ;
2018-03-17 08:50:49 +01:00
}
2017-11-15 15:47:28 +01:00
2018-03-17 08:50:49 +01:00
if ( ! Config :: get ( 'system' , 'ostatus_disabled' )) {
2018-08-11 22:40:44 +02:00
$ostatus = Protocol :: OSTATUS ;
2018-03-17 08:50:49 +01:00
} else {
2018-08-11 22:40:44 +02:00
$ostatus = Protocol :: DFRN ;
2018-03-17 08:50:49 +01:00
}
2017-11-15 15:47:28 +01:00
2018-03-17 08:50:49 +01:00
// check if we search only communities or every contact
if ( $mode === " community " ) {
$extra_sql = " AND `community` " ;
} else {
$extra_sql = " " ;
}
$search .= " % " ;
2018-07-20 14:19:26 +02:00
$results = DBA :: p ( " SELECT `nurl` FROM `gcontact`
2018-10-06 05:17:44 +02:00
WHERE NOT `hide` AND `network` IN ( ? , ? , ? , ? ) AND
2018-03-17 08:50:49 +01:00
(( `last_contact` >= `last_failure` ) OR ( `updated` >= `last_failure` )) AND
( `addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ? ) $extra_sql
GROUP BY `nurl` ORDER BY `nurl` DESC LIMIT 1000 " ,
2018-10-06 05:17:44 +02:00
Protocol :: DFRN , Protocol :: ACTIVITYPUB , $ostatus , $diaspora , $search , $search , $search
2018-03-17 08:50:49 +01:00
);
$gcontacts = [];
2018-07-20 14:19:26 +02:00
while ( $result = DBA :: fetch ( $results )) {
2018-03-17 08:50:49 +01:00
$urlparts = parse_url ( $result [ " nurl " ]);
2017-11-15 15:47:28 +01:00
2018-03-17 08:50:49 +01:00
// Ignore results that look strange.
// For historic reasons the gcontact table does contain some garbage.
if ( ! empty ( $urlparts [ 'query' ]) || ! empty ( $urlparts [ 'fragment' ])) {
continue ;
}
2017-11-15 15:47:28 +01:00
2018-03-17 08:50:49 +01:00
$gcontacts [] = Contact :: getDetailsByURL ( $result [ " nurl " ], local_user ());
2017-11-15 15:47:28 +01:00
}
2018-03-17 08:50:49 +01:00
return $gcontacts ;
2017-11-15 15:47:28 +01:00
}
/**
* @ brief Link the gcontact entry with user , contact and global contact
*
* @ param integer $gcid Global contact ID
* @ param integer $uid User ID
* @ param integer $cid Contact ID
* @ param integer $zcid Global Contact ID
2017-11-19 23:04:40 +01:00
* @ return void
2019-01-06 22:06:53 +01:00
* @ throws Exception
2017-11-15 15:47:28 +01:00
*/
public static function link ( $gcid , $uid = 0 , $cid = 0 , $zcid = 0 )
{
if ( $gcid <= 0 ) {
return ;
}
2018-08-19 14:46:11 +02:00
$condition = [ 'cid' => $cid , 'uid' => $uid , 'gcid' => $gcid , 'zcid' => $zcid ];
DBA :: update ( 'glink' , [ 'updated' => DateTimeFormat :: utcNow ()], $condition , true );
2017-11-15 15:47:28 +01:00
}
/**
* @ brief Sanitize the given gcontact data
*
* Generation :
* 0 : No definition
* 1 : Profiles on this server
* 2 : Contacts of profiles on this server
* 3 : Contacts of contacts of profiles on this server
* 4 : ...
2019-01-06 22:06:53 +01:00
*
* @ param array $gcontact array with gcontact data
2017-11-19 23:04:40 +01:00
* @ return array $gcontact
2019-01-06 22:06:53 +01:00
* @ throws Exception
2017-11-15 15:47:28 +01:00
*/
public static function sanitize ( $gcontact )
{
if ( $gcontact [ 'url' ] == " " ) {
throw new Exception ( 'URL is empty' );
}
$urlparts = parse_url ( $gcontact [ 'url' ]);
if ( ! isset ( $urlparts [ " scheme " ])) {
throw new Exception ( " This ( " . $gcontact [ 'url' ] . " ) doesn't seem to be an url. " );
}
2018-08-07 17:06:51 +02:00
if ( in_array ( $urlparts [ " host " ], [ " twitter.com " , " identi.ca " ])) {
2017-11-15 15:47:28 +01:00
throw new Exception ( 'Contact from a non federated network ignored. (' . $gcontact [ 'url' ] . ')' );
}
// Don't store the statusnet connector as network
2018-08-11 22:40:44 +02:00
// We can't simply set this to Protocol::OSTATUS since the connector could have fetched posts from friendica as well
if ( $gcontact [ 'network' ] == Protocol :: STATUSNET ) {
2017-11-15 15:47:28 +01:00
$gcontact [ 'network' ] = " " ;
}
// Assure that there are no parameter fragments in the profile url
2019-07-02 11:06:48 +02:00
if ( empty ( $gcontact [ " network " ]) || in_array ( $gcontact [ " network " ], Protocol :: FEDERATED )) {
2017-11-15 15:47:28 +01:00
$gcontact [ 'url' ] = self :: cleanContactUrl ( $gcontact [ 'url' ]);
}
2017-11-15 16:53:16 +01:00
$alternate = PortableContact :: alternateOStatusUrl ( $gcontact [ 'url' ]);
2017-11-15 15:47:28 +01:00
// The global contacts should contain the original picture, not the cached one
2018-11-08 17:28:29 +01:00
if (( $gcontact [ 'generation' ] != 1 ) && stristr ( Strings :: normaliseLink ( $gcontact [ 'photo' ]), Strings :: normaliseLink ( System :: baseUrl () . " /photo/ " ))) {
2017-11-15 15:47:28 +01:00
$gcontact [ 'photo' ] = " " ;
}
if ( ! isset ( $gcontact [ 'network' ])) {
2018-08-19 14:46:11 +02:00
$condition = [ " `uid` = 0 AND `nurl` = ? AND `network` != '' AND `network` != ? " ,
2018-11-08 17:28:29 +01:00
Strings :: normaliseLink ( $gcontact [ 'url' ]), Protocol :: STATUSNET ];
2018-08-19 14:46:11 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'network' ], $condition );
if ( DBA :: isResult ( $contact )) {
$gcontact [ 'network' ] = $contact [ " network " ];
2017-11-15 15:47:28 +01:00
}
2018-08-11 22:40:44 +02:00
if (( $gcontact [ 'network' ] == " " ) || ( $gcontact [ 'network' ] == Protocol :: OSTATUS )) {
2018-08-19 14:46:11 +02:00
$condition = [ " `uid` = 0 AND `alias` IN (?, ?) AND `network` != '' AND `network` != ? " ,
2018-11-08 17:28:29 +01:00
$gcontact [ 'url' ], Strings :: normaliseLink ( $gcontact [ 'url' ]), Protocol :: STATUSNET ];
2018-08-19 14:46:11 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'network' ], $condition );
if ( DBA :: isResult ( $contact )) {
$gcontact [ 'network' ] = $contact [ " network " ];
2017-11-15 15:47:28 +01:00
}
}
}
$gcontact [ 'server_url' ] = '' ;
$gcontact [ 'network' ] = '' ;
2018-08-19 14:46:11 +02:00
$fields = [ 'network' , 'updated' , 'server_url' , 'url' , 'addr' ];
2018-11-08 17:28:29 +01:00
$gcnt = DBA :: selectFirst ( 'gcontact' , $fields , [ 'nurl' => Strings :: normaliseLink ( $gcontact [ 'url' ])]);
2018-08-19 14:46:11 +02:00
if ( DBA :: isResult ( $gcnt )) {
if ( ! isset ( $gcontact [ 'network' ]) && ( $gcnt [ " network " ] != Protocol :: STATUSNET )) {
$gcontact [ 'network' ] = $gcnt [ " network " ];
2017-11-15 15:47:28 +01:00
}
2018-10-21 07:53:47 +02:00
if ( $gcontact [ 'updated' ] <= DBA :: NULL_DATETIME ) {
2018-08-19 14:46:11 +02:00
$gcontact [ 'updated' ] = $gcnt [ " updated " ];
2017-11-15 15:47:28 +01:00
}
2018-11-08 17:28:29 +01:00
if ( ! isset ( $gcontact [ 'server_url' ]) && ( Strings :: normaliseLink ( $gcnt [ " server_url " ]) != Strings :: normaliseLink ( $gcnt [ " url " ]))) {
2018-08-19 14:46:11 +02:00
$gcontact [ 'server_url' ] = $gcnt [ " server_url " ];
2017-11-15 15:47:28 +01:00
}
if ( ! isset ( $gcontact [ 'addr' ])) {
2018-08-19 14:46:11 +02:00
$gcontact [ 'addr' ] = $gcnt [ " addr " ];
2017-11-15 15:47:28 +01:00
}
}
if (( ! isset ( $gcontact [ 'network' ]) || ! isset ( $gcontact [ 'name' ]) || ! isset ( $gcontact [ 'addr' ]) || ! isset ( $gcontact [ 'photo' ]) || ! isset ( $gcontact [ 'server_url' ]) || $alternate )
2017-11-15 16:53:16 +01:00
&& PortableContact :: reachable ( $gcontact [ 'url' ], $gcontact [ 'server_url' ], $gcontact [ 'network' ], false )
2017-11-15 15:47:28 +01:00
) {
$data = Probe :: uri ( $gcontact [ 'url' ]);
2018-08-11 22:40:44 +02:00
if ( $data [ " network " ] == Protocol :: PHANTOM ) {
2017-11-15 15:47:28 +01:00
throw new Exception ( 'Probing for URL ' . $gcontact [ 'url' ] . ' failed' );
}
$orig_profile = $gcontact [ 'url' ];
$gcontact [ " server_url " ] = $data [ " baseurl " ];
$gcontact = array_merge ( $gcontact , $data );
2018-08-11 22:40:44 +02:00
if ( $alternate && ( $gcontact [ 'network' ] == Protocol :: OSTATUS )) {
2017-11-15 15:47:28 +01:00
// Delete the old entry - if it exists
2018-11-08 17:28:29 +01:00
if ( DBA :: exists ( 'gcontact' , [ 'nurl' => Strings :: normaliseLink ( $orig_profile )])) {
DBA :: delete ( 'gcontact' , [ 'nurl' => Strings :: normaliseLink ( $orig_profile )]);
2017-11-15 15:47:28 +01:00
}
}
}
if ( ! isset ( $gcontact [ 'name' ]) || ! isset ( $gcontact [ 'photo' ])) {
throw new Exception ( 'No name and photo for URL ' . $gcontact [ 'url' ]);
}
2019-07-02 11:06:48 +02:00
if ( ! in_array ( $gcontact [ 'network' ], Protocol :: FEDERATED )) {
2017-11-15 15:47:28 +01:00
throw new Exception ( 'No federated network (' . $gcontact [ 'network' ] . ') detected for URL ' . $gcontact [ 'url' ]);
}
if ( ! isset ( $gcontact [ 'server_url' ])) {
// We check the server url to be sure that it is a real one
2017-11-15 16:53:16 +01:00
$server_url = PortableContact :: detectServer ( $gcontact [ 'url' ]);
2017-11-15 15:47:28 +01:00
// We are now sure that it is a correct URL. So we use it in the future
if ( $server_url != " " ) {
$gcontact [ 'server_url' ] = $server_url ;
}
}
// The server URL doesn't seem to be valid, so we don't store it.
2017-11-15 16:53:16 +01:00
if ( ! PortableContact :: checkServer ( $gcontact [ 'server_url' ], $gcontact [ 'network' ])) {
2017-11-15 15:47:28 +01:00
$gcontact [ 'server_url' ] = " " ;
}
return $gcontact ;
}
2017-11-19 23:04:40 +01:00
/**
* @ param integer $uid id
* @ param integer $cid id
* @ return integer
2019-01-07 16:24:06 +01:00
* @ throws Exception
2017-11-19 23:04:40 +01:00
*/
2017-11-15 15:47:28 +01:00
public static function countCommonFriends ( $uid , $cid )
{
$r = q (
" SELECT count(*) as `total`
FROM `glink` INNER JOIN `gcontact` on `glink` . `gcid` = `gcontact` . `id`
WHERE `glink` . `cid` = % d AND `glink` . `uid` = % d AND
(( `gcontact` . `last_contact` >= `gcontact` . `last_failure` ) OR
( `gcontact` . `updated` >= `gcontact` . `last_failure` ))
AND `gcontact` . `nurl` IN ( select nurl from contact where uid = % d and self = 0 and blocked = 0 and hidden = 0 and id != % d ) " ,
intval ( $cid ),
intval ( $uid ),
intval ( $uid ),
intval ( $cid )
);
2018-10-29 22:20:46 +01:00
// Logger::log("countCommonFriends: $uid $cid {$r[0]['total']}");
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2017-11-15 15:47:28 +01:00
return $r [ 0 ][ 'total' ];
}
return 0 ;
}
2017-11-19 23:04:40 +01:00
/**
* @ param integer $uid id
* @ param integer $zcid zcid
* @ return integer
2019-01-07 16:24:06 +01:00
* @ throws Exception
2017-11-19 23:04:40 +01:00
*/
2017-11-15 15:47:28 +01:00
public static function countCommonFriendsZcid ( $uid , $zcid )
{
$r = q (
" SELECT count(*) as `total`
FROM `glink` INNER JOIN `gcontact` on `glink` . `gcid` = `gcontact` . `id`
where `glink` . `zcid` = % d
and `gcontact` . `nurl` in ( select nurl from contact where uid = % d and self = 0 and blocked = 0 and hidden = 0 ) " ,
intval ( $zcid ),
intval ( $uid )
);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2017-11-15 15:47:28 +01:00
return $r [ 0 ][ 'total' ];
}
return 0 ;
}
2017-11-19 23:04:40 +01:00
/**
2018-01-05 01:42:48 +01:00
* @ param integer $uid user
* @ param integer $cid cid
2017-11-19 23:04:40 +01:00
* @ param integer $start optional , default 0
* @ param integer $limit optional , default 9999
* @ param boolean $shuffle optional , default false
* @ return object
2019-01-07 16:24:06 +01:00
* @ throws Exception
2017-11-19 23:04:40 +01:00
*/
2017-11-15 15:47:28 +01:00
public static function commonFriends ( $uid , $cid , $start = 0 , $limit = 9999 , $shuffle = false )
{
if ( $shuffle ) {
$sql_extra = " order by rand() " ;
} else {
$sql_extra = " order by `gcontact`.`name` asc " ;
}
$r = q (
" SELECT `gcontact`.*, `contact`.`id` AS `cid`
FROM `glink`
INNER JOIN `gcontact` ON `glink` . `gcid` = `gcontact` . `id`
INNER JOIN `contact` ON `gcontact` . `nurl` = `contact` . `nurl`
WHERE `glink` . `cid` = % d and `glink` . `uid` = % d
AND `contact` . `uid` = % d AND `contact` . `self` = 0 AND `contact` . `blocked` = 0
AND `contact` . `hidden` = 0 AND `contact` . `id` != % d
AND (( `gcontact` . `last_contact` >= `gcontact` . `last_failure` ) OR ( `gcontact` . `updated` >= `gcontact` . `last_failure` ))
$sql_extra LIMIT % d , % d " ,
intval ( $cid ),
intval ( $uid ),
intval ( $uid ),
intval ( $cid ),
intval ( $start ),
intval ( $limit )
);
2018-07-21 14:46:04 +02:00
/// @TODO Check all calling-findings of this function if they properly use DBA::isResult()
2017-11-15 15:47:28 +01:00
return $r ;
}
2017-11-19 23:04:40 +01:00
/**
2018-01-05 01:42:48 +01:00
* @ param integer $uid user
* @ param integer $zcid zcid
2017-11-19 23:04:40 +01:00
* @ param integer $start optional , default 0
* @ param integer $limit optional , default 9999
* @ param boolean $shuffle optional , default false
* @ return object
2019-01-07 16:24:06 +01:00
* @ throws Exception
2017-11-19 23:04:40 +01:00
*/
public static function commonFriendsZcid ( $uid , $zcid , $start = 0 , $limit = 9999 , $shuffle = false )
2017-11-15 15:47:28 +01:00
{
if ( $shuffle ) {
$sql_extra = " order by rand() " ;
} else {
$sql_extra = " order by `gcontact`.`name` asc " ;
}
$r = q (
" SELECT `gcontact`.*
FROM `glink` INNER JOIN `gcontact` on `glink` . `gcid` = `gcontact` . `id`
where `glink` . `zcid` = % d
and `gcontact` . `nurl` in ( select nurl from contact where uid = % d and self = 0 and blocked = 0 and hidden = 0 )
$sql_extra limit % d , % d " ,
intval ( $zcid ),
intval ( $uid ),
intval ( $start ),
intval ( $limit )
);
2018-07-21 14:46:04 +02:00
/// @TODO Check all calling-findings of this function if they properly use DBA::isResult()
2017-11-15 15:47:28 +01:00
return $r ;
}
2017-11-19 23:04:40 +01:00
/**
2018-01-05 01:42:48 +01:00
* @ param integer $uid user
* @ param integer $cid cid
2017-11-19 23:04:40 +01:00
* @ return integer
2019-01-07 16:24:06 +01:00
* @ throws Exception
2017-11-19 23:04:40 +01:00
*/
2017-11-15 15:47:28 +01:00
public static function countAllFriends ( $uid , $cid )
{
$r = q (
" SELECT count(*) as `total`
FROM `glink` INNER JOIN `gcontact` on `glink` . `gcid` = `gcontact` . `id`
where `glink` . `cid` = % d and `glink` . `uid` = % d AND
(( `gcontact` . `last_contact` >= `gcontact` . `last_failure` ) OR ( `gcontact` . `updated` >= `gcontact` . `last_failure` )) " ,
intval ( $cid ),
intval ( $uid )
);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2017-11-15 15:47:28 +01:00
return $r [ 0 ][ 'total' ];
}
2017-11-19 23:06:18 +01:00
2017-11-15 15:47:28 +01:00
return 0 ;
}
2017-11-19 23:04:40 +01:00
/**
2018-01-05 01:42:48 +01:00
* @ param integer $uid user
* @ param integer $cid cid
2017-11-19 23:04:40 +01:00
* @ param integer $start optional , default 0
* @ param integer $limit optional , default 80
2018-01-05 01:42:48 +01:00
* @ return array
2019-01-07 16:24:06 +01:00
* @ throws Exception
2017-11-19 23:04:40 +01:00
*/
2017-11-15 15:47:28 +01:00
public static function allFriends ( $uid , $cid , $start = 0 , $limit = 80 )
{
$r = q (
" SELECT `gcontact`.*, `contact`.`id` AS `cid`
FROM `glink`
INNER JOIN `gcontact` on `glink` . `gcid` = `gcontact` . `id`
LEFT JOIN `contact` ON `contact` . `nurl` = `gcontact` . `nurl` AND `contact` . `uid` = % d
WHERE `glink` . `cid` = % d AND `glink` . `uid` = % d AND
(( `gcontact` . `last_contact` >= `gcontact` . `last_failure` ) OR ( `gcontact` . `updated` >= `gcontact` . `last_failure` ))
ORDER BY `gcontact` . `name` ASC LIMIT % d , % d " ,
intval ( $uid ),
intval ( $cid ),
intval ( $uid ),
intval ( $start ),
intval ( $limit )
);
2018-07-21 14:46:04 +02:00
/// @TODO Check all calling-findings of this function if they properly use DBA::isResult()
2017-11-15 15:47:28 +01:00
return $r ;
}
2017-11-19 23:04:40 +01:00
/**
2019-01-07 07:07:42 +01:00
* @ param int $uid user
2017-11-19 23:04:40 +01:00
* @ param integer $start optional , default 0
* @ param integer $limit optional , default 80
* @ return array
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2017-11-19 23:04:40 +01:00
*/
2017-11-15 15:47:28 +01:00
public static function suggestionQuery ( $uid , $start = 0 , $limit = 80 )
{
if ( ! $uid ) {
2018-01-15 14:05:12 +01:00
return [];
2017-11-15 15:47:28 +01:00
}
/*
* Uncommented because the result of the queries are to big to store it in the cache .
* We need to decide if we want to change the db column type or if we want to delete it .
*/
//$list = Cache::get("suggestion_query:".$uid.":".$start.":".$limit);
//if (!is_null($list)) {
// return $list;
//}
2018-11-13 06:52:21 +01:00
$network = [ Protocol :: DFRN , Protocol :: ACTIVITYPUB ];
2017-11-15 15:47:28 +01:00
if ( Config :: get ( 'system' , 'diaspora_enabled' )) {
2018-08-11 22:40:44 +02:00
$network [] = Protocol :: DIASPORA ;
2017-11-15 15:47:28 +01:00
}
if ( ! Config :: get ( 'system' , 'ostatus_disabled' )) {
2018-08-11 22:40:44 +02:00
$network [] = Protocol :: OSTATUS ;
2017-11-15 15:47:28 +01:00
}
$sql_network = implode ( " ', ' " , $network );
$sql_network = " ' " . $sql_network . " ' " ;
/// @todo This query is really slow
// By now we cache the data for five minutes
$r = q (
" SELECT count(glink.gcid) as `total`, gcontact.* from gcontact
INNER JOIN `glink` ON `glink` . `gcid` = `gcontact` . `id`
where uid = % d and not gcontact . nurl in ( select nurl from contact where uid = % d )
AND NOT `gcontact` . `name` IN ( SELECT `name` FROM `contact` WHERE `uid` = % d )
AND NOT `gcontact` . `id` IN ( SELECT `gcid` FROM `gcign` WHERE `uid` = % d )
2018-11-13 06:52:21 +01:00
AND `gcontact` . `updated` >= '%s' AND NOT `gcontact` . `hide`
2017-11-15 15:47:28 +01:00
AND `gcontact` . `last_contact` >= `gcontact` . `last_failure`
AND `gcontact` . `network` IN ( % s )
GROUP BY `glink` . `gcid` ORDER BY `gcontact` . `updated` DESC , `total` DESC LIMIT % d , % d " ,
intval ( $uid ),
intval ( $uid ),
intval ( $uid ),
intval ( $uid ),
2018-10-21 07:53:47 +02:00
DBA :: NULL_DATETIME ,
2017-11-15 15:47:28 +01:00
$sql_network ,
intval ( $start ),
intval ( $limit )
);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r ) && count ( $r ) >= ( $limit - 1 )) {
2017-11-15 15:47:28 +01:00
/*
* Uncommented because the result of the queries are to big to store it in the cache .
* We need to decide if we want to change the db column type or if we want to delete it .
*/
2018-10-20 18:19:55 +02:00
//Cache::set("suggestion_query:".$uid.":".$start.":".$limit, $r, Cache::FIVE_MINUTES);
2017-11-15 15:47:28 +01:00
return $r ;
}
$r2 = q (
" SELECT gcontact.* FROM gcontact
INNER JOIN `glink` ON `glink` . `gcid` = `gcontact` . `id`
WHERE `glink` . `uid` = 0 AND `glink` . `cid` = 0 AND `glink` . `zcid` = 0 AND NOT `gcontact` . `nurl` IN ( SELECT `nurl` FROM `contact` WHERE `uid` = % d )
AND NOT `gcontact` . `name` IN ( SELECT `name` FROM `contact` WHERE `uid` = % d )
AND NOT `gcontact` . `id` IN ( SELECT `gcid` FROM `gcign` WHERE `uid` = % d )
AND `gcontact` . `updated` >= '%s'
AND `gcontact` . `last_contact` >= `gcontact` . `last_failure`
AND `gcontact` . `network` IN ( % s )
ORDER BY rand () LIMIT % d , % d " ,
intval ( $uid ),
intval ( $uid ),
intval ( $uid ),
2018-10-21 07:53:47 +02:00
DBA :: NULL_DATETIME ,
2017-11-15 15:47:28 +01:00
$sql_network ,
intval ( $start ),
intval ( $limit )
);
2018-01-15 14:05:12 +01:00
$list = [];
2017-11-15 15:47:28 +01:00
foreach ( $r2 as $suggestion ) {
$list [ $suggestion [ " nurl " ]] = $suggestion ;
}
foreach ( $r as $suggestion ) {
$list [ $suggestion [ " nurl " ]] = $suggestion ;
}
while ( sizeof ( $list ) > ( $limit )) {
array_pop ( $list );
}
/*
* Uncommented because the result of the queries are to big to store it in the cache .
* We need to decide if we want to change the db column type or if we want to delete it .
*/
2018-10-20 18:19:55 +02:00
//Cache::set("suggestion_query:".$uid.":".$start.":".$limit, $list, Cache::FIVE_MINUTES);
2017-11-15 15:47:28 +01:00
return $list ;
}
2017-11-19 23:04:40 +01:00
/**
* @ return void
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2017-11-19 23:04:40 +01:00
*/
2017-11-15 15:47:28 +01:00
public static function updateSuggestions ()
{
2018-01-15 14:05:12 +01:00
$done = [];
2017-11-15 15:47:28 +01:00
/// @TODO Check if it is really neccessary to poll the own server
PortableContact :: loadWorker ( 0 , 0 , 0 , System :: baseUrl () . '/poco' );
$done [] = System :: baseUrl () . '/poco' ;
if ( strlen ( Config :: get ( 'system' , 'directory' ))) {
2018-01-27 17:13:41 +01:00
$x = Network :: fetchUrl ( get_server () . " /pubsites " );
2018-07-11 08:05:22 +02:00
if ( ! empty ( $x )) {
2017-11-15 15:47:28 +01:00
$j = json_decode ( $x );
2018-07-11 08:05:22 +02:00
if ( ! empty ( $j -> entries )) {
2017-11-15 15:47:28 +01:00
foreach ( $j -> entries as $entry ) {
2017-11-15 16:53:16 +01:00
PortableContact :: checkServer ( $entry -> url );
2017-11-15 15:47:28 +01:00
$url = $entry -> url . '/poco' ;
2018-07-11 08:05:22 +02:00
if ( ! in_array ( $url , $done )) {
PortableContact :: loadWorker ( 0 , 0 , 0 , $url );
$done [] = $url ;
2017-11-15 15:47:28 +01:00
}
}
}
}
}
// Query your contacts from Friendica and Redmatrix/Hubzilla for their contacts
$r = q (
" SELECT DISTINCT(`poco`) AS `poco` FROM `contact` WHERE `network` IN ('%s', '%s') " ,
2018-08-11 22:40:44 +02:00
DBA :: escape ( Protocol :: DFRN ),
DBA :: escape ( Protocol :: DIASPORA )
2017-11-15 15:47:28 +01:00
);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2017-11-15 15:47:28 +01:00
foreach ( $r as $rr ) {
$base = substr ( $rr [ 'poco' ], 0 , strrpos ( $rr [ 'poco' ], '/' ));
if ( ! in_array ( $base , $done )) {
PortableContact :: loadWorker ( 0 , 0 , 0 , $base );
}
}
}
}
/**
* @ brief Removes unwanted parts from a contact url
*
* @ param string $url Contact url
*
* @ return string Contact url with the wanted parts
2019-01-06 22:06:53 +01:00
* @ throws Exception
2017-11-15 15:47:28 +01:00
*/
public static function cleanContactUrl ( $url )
{
$parts = parse_url ( $url );
if ( ! isset ( $parts [ " scheme " ]) || ! isset ( $parts [ " host " ])) {
return $url ;
}
$new_url = $parts [ " scheme " ] . " :// " . $parts [ " host " ];
if ( isset ( $parts [ " port " ])) {
$new_url .= " : " . $parts [ " port " ];
}
if ( isset ( $parts [ " path " ])) {
$new_url .= $parts [ " path " ];
}
if ( $new_url != $url ) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Cleaned contact url " . $url . " to " . $new_url . " - Called by: " . System :: callstack (), Logger :: DEBUG );
2017-11-15 15:47:28 +01:00
}
return $new_url ;
}
/**
* @ brief Replace alternate OStatus user format with the primary one
*
2017-12-17 21:27:50 +01:00
* @ param array $contact contact array ( called by reference )
2017-11-19 23:04:40 +01:00
* @ return void
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-11-15 15:47:28 +01:00
*/
public static function fixAlternateContactAddress ( & $contact )
{
2018-08-11 22:40:44 +02:00
if (( $contact [ " network " ] == Protocol :: OSTATUS ) && PortableContact :: alternateOStatusUrl ( $contact [ " url " ])) {
2017-11-15 15:47:28 +01:00
$data = Probe :: uri ( $contact [ " url " ]);
2018-08-11 22:40:44 +02:00
if ( $contact [ " network " ] == Protocol :: OSTATUS ) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Fix primary url from " . $contact [ " url " ] . " to " . $data [ " url " ] . " - Called by: " . System :: callstack (), Logger :: DEBUG );
2017-11-15 15:47:28 +01:00
$contact [ " url " ] = $data [ " url " ];
$contact [ " addr " ] = $data [ " addr " ];
$contact [ " alias " ] = $data [ " alias " ];
$contact [ " server_url " ] = $data [ " baseurl " ];
}
}
}
/**
* @ brief Fetch the gcontact id , add an entry if not existed
*
2017-12-17 21:27:50 +01:00
* @ param array $contact contact array
2017-11-15 15:47:28 +01:00
*
* @ return bool | int Returns false if not found , integer if contact was found
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-11-15 15:47:28 +01:00
*/
public static function getId ( $contact )
{
$gcontact_id = 0 ;
$doprobing = false ;
2018-02-14 06:05:00 +01:00
$last_failure_str = '' ;
$last_contact_str = '' ;
2017-11-15 15:47:28 +01:00
2018-07-08 11:37:05 +02:00
if ( empty ( $contact [ " network " ])) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Empty network for contact url " . $contact [ " url " ] . " - Called by: " . System :: callstack (), Logger :: DEBUG );
2018-07-08 11:37:05 +02:00
return false ;
}
2018-08-11 22:40:44 +02:00
if ( in_array ( $contact [ " network " ], [ Protocol :: PHANTOM ])) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Invalid network for contact url " . $contact [ " url " ] . " - Called by: " . System :: callstack (), Logger :: DEBUG );
2017-11-15 15:47:28 +01:00
return false ;
}
2018-08-11 22:40:44 +02:00
if ( $contact [ " network " ] == Protocol :: STATUSNET ) {
$contact [ " network " ] = Protocol :: OSTATUS ;
2017-11-15 15:47:28 +01:00
}
// All new contacts are hidden by default
if ( ! isset ( $contact [ " hide " ])) {
$contact [ " hide " ] = true ;
}
// Replace alternate OStatus user format with the primary one
self :: fixAlternateContactAddress ( $contact );
// Remove unwanted parts from the contact url (e.g. "?zrl=...")
2019-07-02 11:06:48 +02:00
if ( in_array ( $contact [ " network " ], Protocol :: FEDERATED )) {
2017-11-15 15:47:28 +01:00
$contact [ " url " ] = self :: cleanContactUrl ( $contact [ " url " ]);
}
2018-07-20 14:19:26 +02:00
DBA :: lock ( 'gcontact' );
2018-08-19 14:46:11 +02:00
$fields = [ 'id' , 'last_contact' , 'last_failure' , 'network' ];
2018-11-08 17:28:29 +01:00
$gcnt = DBA :: selectFirst ( 'gcontact' , $fields , [ 'nurl' => Strings :: normaliseLink ( $contact [ " url " ])]);
2018-08-19 14:46:11 +02:00
if ( DBA :: isResult ( $gcnt )) {
$gcontact_id = $gcnt [ " id " ];
2017-11-15 15:47:28 +01:00
// Update every 90 days
2018-08-19 14:46:11 +02:00
if ( in_array ( $gcnt [ " network " ], [ Protocol :: DFRN , Protocol :: DIASPORA , Protocol :: OSTATUS , " " ])) {
$last_failure_str = $gcnt [ " last_failure " ];
$last_failure = strtotime ( $gcnt [ " last_failure " ]);
$last_contact_str = $gcnt [ " last_contact " ];
$last_contact = strtotime ( $gcnt [ " last_contact " ]);
2017-11-15 15:47:28 +01:00
$doprobing = ((( time () - $last_contact ) > ( 90 * 86400 )) && (( time () - $last_failure ) > ( 90 * 86400 )));
}
} else {
2018-07-31 18:39:23 +02:00
$contact [ 'location' ] = defaults ( $contact , 'location' , '' );
$contact [ 'about' ] = defaults ( $contact , 'about' , '' );
$contact [ 'generation' ] = defaults ( $contact , 'generation' , 0 );
2017-11-15 15:47:28 +01:00
q (
" INSERT INTO `gcontact` (`name`, `nick`, `addr` , `network`, `url`, `nurl`, `photo`, `created`, `updated`, `location`, `about`, `hide`, `generation`)
VALUES ( '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , % d , % d ) " ,
2018-07-21 15:10:13 +02:00
DBA :: escape ( $contact [ " name " ]),
DBA :: escape ( $contact [ " nick " ]),
DBA :: escape ( $contact [ " addr " ]),
DBA :: escape ( $contact [ " network " ]),
DBA :: escape ( $contact [ " url " ]),
2018-11-08 17:28:29 +01:00
DBA :: escape ( Strings :: normaliseLink ( $contact [ " url " ])),
2018-07-21 15:10:13 +02:00
DBA :: escape ( $contact [ " photo " ]),
DBA :: escape ( DateTimeFormat :: utcNow ()),
DBA :: escape ( DateTimeFormat :: utcNow ()),
DBA :: escape ( $contact [ " location " ]),
DBA :: escape ( $contact [ " about " ]),
2017-11-15 15:47:28 +01:00
intval ( $contact [ " hide " ]),
intval ( $contact [ " generation " ])
);
2018-11-08 17:28:29 +01:00
$condition = [ 'nurl' => Strings :: normaliseLink ( $contact [ " url " ])];
2018-08-19 14:46:11 +02:00
$cnt = DBA :: selectFirst ( 'gcontact' , [ 'id' , 'network' ], $condition , [ 'order' => [ 'id' ]]);
if ( DBA :: isResult ( $cnt )) {
$gcontact_id = $cnt [ " id " ];
$doprobing = in_array ( $cnt [ " network " ], [ Protocol :: DFRN , Protocol :: DIASPORA , Protocol :: OSTATUS , " " ]);
2017-11-15 15:47:28 +01:00
}
}
2018-07-20 14:19:26 +02:00
DBA :: unlock ();
2017-11-15 15:47:28 +01:00
if ( $doprobing ) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Last Contact: " . $last_contact_str . " - Last Failure: " . $last_failure_str . " - Checking: " . $contact [ " url " ], Logger :: DEBUG );
2017-11-19 17:25:13 +01:00
Worker :: add ( PRIORITY_LOW , 'GProbe' , $contact [ " url " ]);
2017-11-15 15:47:28 +01:00
}
return $gcontact_id ;
}
/**
* @ brief Updates the gcontact table from a given array
*
2017-12-17 21:27:50 +01:00
* @ param array $contact contact array
2017-11-15 15:47:28 +01:00
*
* @ return bool | int Returns false if not found , integer if contact was found
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-11-15 15:47:28 +01:00
*/
public static function update ( $contact )
{
// Check for invalid "contact-type" value
if ( isset ( $contact [ 'contact-type' ]) && ( intval ( $contact [ 'contact-type' ]) < 0 )) {
$contact [ 'contact-type' ] = 0 ;
}
/// @todo update contact table as well
$gcontact_id = self :: getId ( $contact );
if ( ! $gcontact_id ) {
return false ;
}
2018-01-11 09:26:30 +01:00
$public_contact = q (
2017-11-15 15:47:28 +01:00
" SELECT `name`, `nick`, `photo`, `location`, `about`, `addr`, `generation`, `birthday`, `gender`, `keywords`,
`contact-type` , `hide` , `nsfw` , `network` , `alias` , `notify` , `server_url` , `connect` , `updated` , `url`
FROM `gcontact` WHERE `id` = % d LIMIT 1 " ,
intval ( $gcontact_id )
);
// Get all field names
2018-01-15 14:05:12 +01:00
$fields = [];
2018-01-11 09:26:30 +01:00
foreach ( $public_contact [ 0 ] as $field => $data ) {
2017-11-15 15:47:28 +01:00
$fields [ $field ] = $data ;
}
unset ( $fields [ " url " ]);
unset ( $fields [ " updated " ]);
unset ( $fields [ " hide " ]);
// Bugfix: We had an error in the storing of keywords which lead to the "0"
// This value is still transmitted via poco.
2018-07-10 14:27:56 +02:00
if ( ! empty ( $contact [ " keywords " ]) && ( $contact [ " keywords " ] == " 0 " )) {
2017-11-15 15:47:28 +01:00
unset ( $contact [ " keywords " ]);
}
2018-07-10 14:27:56 +02:00
if ( ! empty ( $public_contact [ 0 ][ " keywords " ]) && ( $public_contact [ 0 ][ " keywords " ] == " 0 " )) {
2018-01-11 09:26:30 +01:00
$public_contact [ 0 ][ " keywords " ] = " " ;
2017-11-15 15:47:28 +01:00
}
// assign all unassigned fields from the database entry
foreach ( $fields as $field => $data ) {
if ( ! isset ( $contact [ $field ]) || ( $contact [ $field ] == " " )) {
2018-01-11 09:26:30 +01:00
$contact [ $field ] = $public_contact [ 0 ][ $field ];
2017-11-15 15:47:28 +01:00
}
}
if ( ! isset ( $contact [ " hide " ])) {
2018-01-11 09:26:30 +01:00
$contact [ " hide " ] = $public_contact [ 0 ][ " hide " ];
2017-11-15 15:47:28 +01:00
}
2018-01-11 09:26:30 +01:00
$fields [ " hide " ] = $public_contact [ 0 ][ " hide " ];
2017-11-15 15:47:28 +01:00
2018-08-11 22:40:44 +02:00
if ( $contact [ " network " ] == Protocol :: STATUSNET ) {
$contact [ " network " ] = Protocol :: OSTATUS ;
2017-11-15 15:47:28 +01:00
}
// Replace alternate OStatus user format with the primary one
self :: fixAlternateContactAddress ( $contact );
if ( ! isset ( $contact [ " updated " ])) {
2018-07-21 14:25:11 +02:00
$contact [ " updated " ] = DateTimeFormat :: utcNow ();
2017-11-15 15:47:28 +01:00
}
2018-08-11 22:40:44 +02:00
if ( $contact [ " network " ] == Protocol :: TWITTER ) {
2017-11-15 15:47:28 +01:00
$contact [ " server_url " ] = 'http://twitter.com' ;
}
if ( $contact [ " server_url " ] == " " ) {
$data = Probe :: uri ( $contact [ " url " ]);
2018-08-11 22:40:44 +02:00
if ( $data [ " network " ] != Protocol :: PHANTOM ) {
2017-11-15 15:47:28 +01:00
$contact [ " server_url " ] = $data [ 'baseurl' ];
}
} else {
2018-11-08 17:28:29 +01:00
$contact [ " server_url " ] = Strings :: normaliseLink ( $contact [ " server_url " ]);
2017-11-15 15:47:28 +01:00
}
if (( $contact [ " addr " ] == " " ) && ( $contact [ " server_url " ] != " " ) && ( $contact [ " nick " ] != " " )) {
$hostname = str_replace ( " http:// " , " " , $contact [ " server_url " ]);
$contact [ " addr " ] = $contact [ " nick " ] . " @ " . $hostname ;
}
// Check if any field changed
$update = false ;
unset ( $fields [ " generation " ]);
2018-01-11 09:26:30 +01:00
if ((( $contact [ " generation " ] > 0 ) && ( $contact [ " generation " ] <= $public_contact [ 0 ][ " generation " ])) || ( $public_contact [ 0 ][ " generation " ] == 0 )) {
2017-11-15 15:47:28 +01:00
foreach ( $fields as $field => $data ) {
2018-01-11 09:26:30 +01:00
if ( $contact [ $field ] != $public_contact [ 0 ][ $field ]) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Difference for contact " . $contact [ " url " ] . " in field ' " . $field . " '. New value: ' " . $contact [ $field ] . " ', old value ' " . $public_contact [ 0 ][ $field ] . " ' " , Logger :: DEBUG );
2017-11-15 15:47:28 +01:00
$update = true ;
}
}
2018-01-11 09:26:30 +01:00
if ( $contact [ " generation " ] < $public_contact [ 0 ][ " generation " ]) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Difference for contact " . $contact [ " url " ] . " in field 'generation'. new value: ' " . $contact [ " generation " ] . " ', old value ' " . $public_contact [ 0 ][ " generation " ] . " ' " , Logger :: DEBUG );
2017-11-15 15:47:28 +01:00
$update = true ;
}
}
if ( $update ) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Update gcontact for " . $contact [ " url " ], Logger :: DEBUG );
2018-01-15 14:05:12 +01:00
$condition = [ '`nurl` = ? AND (`generation` = 0 OR `generation` >= ?)' ,
2018-11-08 17:28:29 +01:00
Strings :: normaliseLink ( $contact [ " url " ]), $contact [ " generation " ]];
2018-07-21 14:25:11 +02:00
$contact [ " updated " ] = DateTimeFormat :: utc ( $contact [ " updated " ]);
2017-11-15 15:47:28 +01:00
2018-01-15 14:05:12 +01:00
$updated = [ 'photo' => $contact [ 'photo' ], 'name' => $contact [ 'name' ],
2017-11-15 15:47:28 +01:00
'nick' => $contact [ 'nick' ], 'addr' => $contact [ 'addr' ],
'network' => $contact [ 'network' ], 'birthday' => $contact [ 'birthday' ],
'gender' => $contact [ 'gender' ], 'keywords' => $contact [ 'keywords' ],
'hide' => $contact [ 'hide' ], 'nsfw' => $contact [ 'nsfw' ],
'contact-type' => $contact [ 'contact-type' ], 'alias' => $contact [ 'alias' ],
'notify' => $contact [ 'notify' ], 'url' => $contact [ 'url' ],
'location' => $contact [ 'location' ], 'about' => $contact [ 'about' ],
'generation' => $contact [ 'generation' ], 'updated' => $contact [ 'updated' ],
2018-01-15 14:05:12 +01:00
'server_url' => $contact [ 'server_url' ], 'connect' => $contact [ 'connect' ]];
2017-11-15 15:47:28 +01:00
2018-07-20 14:19:26 +02:00
DBA :: update ( 'gcontact' , $updated , $condition , $fields );
2019-07-02 11:06:48 +02:00
}
2017-11-15 15:47:28 +01:00
2019-07-02 11:06:48 +02:00
return $gcontact_id ;
}
2018-03-24 23:27:04 +01:00
2019-07-02 11:06:48 +02:00
/**
* @ brief Updates the gcontact entry from a given public contact id
*
* @ param integer $cid contact id
* @ return void
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
*/
2019-07-03 07:46:35 +02:00
public static function updateFromPublicContactID ( $cid )
{
self :: updateFromPublicContact ([ 'id' => $cid ]);
}
/**
* @ brief Updates the gcontact entry from a given public contact url
*
* @ param string $url contact url
2019-07-04 06:08:55 +02:00
* @ return integer gcontact id
2019-07-03 07:46:35 +02:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
*/
public static function updateFromPublicContactURL ( $url )
{
2019-07-04 06:08:55 +02:00
return self :: updateFromPublicContact ([ 'nurl' => Strings :: normaliseLink ( $url )]);
2019-07-03 07:46:35 +02:00
}
/**
* @ brief Helper function for updateFromPublicContactID and updateFromPublicContactURL
*
* @ param array $condition contact condition
2019-07-04 06:08:55 +02:00
* @ return integer gcontact id
2019-07-03 07:46:35 +02:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
*/
private static function updateFromPublicContact ( $condition )
2019-07-02 11:06:48 +02:00
{
$fields = [ 'name' , 'nick' , 'url' , 'nurl' , 'location' , 'about' , 'keywords' , 'gender' ,
2019-07-02 16:46:04 +02:00
'bd' , 'contact-type' , 'network' , 'addr' , 'notify' , 'alias' , 'archive' , 'term-date' ,
2019-07-04 06:08:55 +02:00
'created' , 'updated' , 'avatar' , 'success_update' , 'failure_update' , 'forum' , 'prv' ,
'baseurl' , 'sensitive' , 'unsearchable' ];
2019-07-03 07:46:35 +02:00
$contact = DBA :: selectFirst ( 'contact' , $fields , array_merge ( $condition , [ 'uid' => 0 , 'network' => Protocol :: FEDERATED ]));
2019-07-02 11:06:48 +02:00
if ( ! DBA :: isResult ( $contact )) {
2019-07-04 06:08:55 +02:00
return 0 ;
2019-07-02 11:06:48 +02:00
}
2018-03-24 23:27:04 +01:00
2019-07-02 11:06:48 +02:00
$fields = [ 'name' , 'nick' , 'url' , 'nurl' , 'location' , 'about' , 'keywords' , 'gender' , 'generation' ,
'birthday' , 'contact-type' , 'network' , 'addr' , 'notify' , 'alias' , 'archived' , 'archive_date' ,
2019-07-04 06:08:55 +02:00
'created' , 'updated' , 'photo' , 'last_contact' , 'last_failure' , 'community' , 'connect' ,
'server_url' , 'nsfw' , 'hide' , 'id' ];
2019-07-02 11:06:48 +02:00
$old_gcontact = DBA :: selectFirst ( 'gcontact' , $fields , [ 'nurl' => $contact [ 'nurl' ]]);
$do_insert = ! DBA :: isResult ( $old_gcontact );
if ( $do_insert ) {
$old_gcontact = [];
}
2019-07-04 21:31:42 +02:00
$gcontact = [];
// These fields are identical in both contact and gcontact
$fields = [ 'name' , 'nick' , 'url' , 'nurl' , 'location' , 'about' , 'keywords' , 'gender' ,
'contact-type' , 'network' , 'addr' , 'notify' , 'alias' , 'created' , 'updated' ];
foreach ( $fields as $field ) {
$gcontact [ $field ] = $contact [ $field ];
}
2019-07-02 11:06:48 +02:00
// These fields are having different names but the same content
2019-07-14 18:04:52 +02:00
$gcontact [ 'server_url' ] = $contact [ 'baseurl' ] ? ? '' ; // "baseurl" can be null, "server_url" not
2019-07-04 21:31:42 +02:00
$gcontact [ 'nsfw' ] = $contact [ 'sensitive' ];
$gcontact [ 'hide' ] = $contact [ 'unsearchable' ];
$gcontact [ 'archived' ] = $contact [ 'archive' ];
$gcontact [ 'archive_date' ] = $contact [ 'term-date' ];
$gcontact [ 'birthday' ] = $contact [ 'bd' ];
$gcontact [ 'photo' ] = $contact [ 'avatar' ];
$gcontact [ 'last_contact' ] = $contact [ 'success_update' ];
$gcontact [ 'last_failure' ] = $contact [ 'failure_update' ];
$gcontact [ 'community' ] = ( $contact [ 'forum' ] || $contact [ 'prv' ]);
2019-07-02 11:06:48 +02:00
foreach ([ 'last_contact' , 'last_failure' , 'updated' ] as $field ) {
if ( ! empty ( $old_gcontact [ $field ]) && ( $old_gcontact [ $field ] >= $gcontact [ $field ])) {
unset ( $gcontact [ $field ]);
2017-11-15 15:47:28 +01:00
}
}
2019-07-02 16:46:04 +02:00
if ( ! $gcontact [ 'archived' ]) {
2019-07-02 11:06:48 +02:00
$gcontact [ 'archive_date' ] = DBA :: NULL_DATETIME ;
}
if ( ! empty ( $old_gcontact [ 'created' ]) && ( $old_gcontact [ 'created' ] > DBA :: NULL_DATETIME )
&& ( $old_gcontact [ 'created' ] <= $gcontact [ 'created' ])) {
unset ( $gcontact [ 'created' ]);
}
if ( empty ( $gcontact [ 'birthday' ]) && ( $gcontact [ 'birthday' ] <= DBA :: NULL_DATETIME )) {
unset ( $gcontact [ 'birthday' ]);
}
if ( empty ( $old_gcontact [ 'generation' ]) || ( $old_gcontact [ 'generation' ] > 2 )) {
$gcontact [ 'generation' ] = 2 ; // We fetched the data directly from the other server
}
if ( ! $do_insert ) {
DBA :: update ( 'gcontact' , $gcontact , [ 'nurl' => $contact [ 'nurl' ]], $old_gcontact );
2019-07-04 06:08:55 +02:00
return $old_gcontact [ 'id' ];
2019-07-02 11:06:48 +02:00
} elseif ( ! $gcontact [ 'archived' ]) {
DBA :: insert ( 'gcontact' , $gcontact );
2019-07-04 06:08:55 +02:00
return DBA :: lastInsertId ();
2019-07-02 11:06:48 +02:00
}
2017-11-15 15:47:28 +01:00
}
/**
* @ brief Updates the gcontact entry from probe
*
2019-07-02 11:06:48 +02:00
* @ param string $url profile link
* @ param boolean $force Optional forcing of network probing ( otherwise we use the cached data )
2017-11-19 23:04:40 +01:00
* @ return void
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-11-15 15:47:28 +01:00
*/
2019-07-02 11:06:48 +02:00
public static function updateFromProbe ( $url , $force = false )
2017-11-15 15:47:28 +01:00
{
2019-07-02 11:06:48 +02:00
$data = Probe :: uri ( $url , $force );
2017-11-15 15:47:28 +01:00
2018-08-11 22:40:44 +02:00
if ( in_array ( $data [ " network " ], [ Protocol :: PHANTOM ])) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Invalid network for contact url " . $data [ " url " ] . " - Called by: " . System :: callstack (), Logger :: DEBUG );
2017-11-15 15:47:28 +01:00
return ;
}
$data [ " server_url " ] = $data [ " baseurl " ];
self :: update ( $data );
}
/**
* @ brief Update the gcontact entry for a given user id
*
* @ param int $uid User ID
2019-01-06 22:06:53 +01:00
* @ return bool
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-11-15 15:47:28 +01:00
*/
public static function updateForUser ( $uid )
{
$r = q (
" SELECT `profile`.`locality`, `profile`.`region`, `profile`.`country-name`,
`profile` . `name` , `profile` . `about` , `profile` . `gender` ,
`profile` . `pub_keywords` , `profile` . `dob` , `profile` . `photo` ,
`profile` . `net-publish` , `user` . `nickname` , `user` . `hidewall` ,
`contact` . `notify` , `contact` . `url` , `contact` . `addr`
FROM `profile`
INNER JOIN `user` ON `user` . `uid` = `profile` . `uid`
INNER JOIN `contact` ON `contact` . `uid` = `profile` . `uid`
WHERE `profile` . `uid` = % d AND `profile` . `is-default` AND `contact` . `self` " ,
intval ( $uid )
);
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
if ( ! DBA :: isResult ( $r )) {
2018-10-30 14:58:45 +01:00
Logger :: log ( 'Cannot find user with uid=' . $uid , Logger :: INFO );
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
return false ;
}
2017-11-19 23:03:39 +01:00
$location = Profile :: formatLocation (
2018-01-15 14:05:12 +01:00
[ " locality " => $r [ 0 ][ " locality " ], " region " => $r [ 0 ][ " region " ], " country-name " => $r [ 0 ][ " country-name " ]]
2017-11-15 15:47:28 +01:00
);
// The "addr" field was added in 3.4.3 so it can be empty for older users
if ( $r [ 0 ][ " addr " ] != " " ) {
2018-01-15 14:05:12 +01:00
$addr = $r [ 0 ][ " nickname " ] . '@' . str_replace ([ " http:// " , " https:// " ], " " , System :: baseUrl ());
2017-11-15 15:47:28 +01:00
} else {
$addr = $r [ 0 ][ " addr " ];
}
2018-01-15 14:05:12 +01:00
$gcontact = [ " name " => $r [ 0 ][ " name " ], " location " => $location , " about " => $r [ 0 ][ " about " ],
2017-11-15 15:47:28 +01:00
" gender " => $r [ 0 ][ " gender " ], " keywords " => $r [ 0 ][ " pub_keywords " ],
" birthday " => $r [ 0 ][ " dob " ], " photo " => $r [ 0 ][ " photo " ],
" notify " => $r [ 0 ][ " notify " ], " url " => $r [ 0 ][ " url " ],
" hide " => ( $r [ 0 ][ " hidewall " ] || ! $r [ 0 ][ " net-publish " ]),
" nick " => $r [ 0 ][ " nickname " ], " addr " => $addr ,
" connect " => $addr , " server_url " => System :: baseUrl (),
2018-08-11 22:40:44 +02:00
" generation " => 1 , " network " => Protocol :: DFRN ];
2017-11-15 15:47:28 +01:00
self :: update ( $gcontact );
}
/**
* @ brief Fetches users of given GNU Social server
*
2018-01-17 20:22:38 +01:00
* If the " Statistics " addon is enabled ( See http :// gstools . org / for details ) we query user data with this .
2017-11-15 15:47:28 +01:00
*
2017-12-17 21:27:50 +01:00
* @ param string $server Server address
2019-01-06 22:06:53 +01:00
* @ return bool
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-11-15 15:47:28 +01:00
*/
2017-11-15 18:02:08 +01:00
public static function fetchGsUsers ( $server )
2017-11-15 15:47:28 +01:00
{
2018-10-30 14:58:45 +01:00
Logger :: log ( " Fetching users from GNU Social server " . $server , Logger :: DEBUG );
2017-11-15 15:47:28 +01:00
$url = $server . " /main/statistics " ;
2018-10-10 21:08:43 +02:00
$curlResult = Network :: curl ( $url );
if ( ! $curlResult -> isSuccess ()) {
2017-11-15 15:47:28 +01:00
return false ;
}
2018-10-10 21:08:43 +02:00
$statistics = json_decode ( $curlResult -> getBody ());
2017-11-15 15:47:28 +01:00
2018-12-15 10:32:47 +01:00
if ( ! empty ( $statistics -> config -> instance_address )) {
if ( ! empty ( $statistics -> config -> instance_with_ssl )) {
2017-11-15 15:47:28 +01:00
$server = " https:// " ;
} else {
$server = " http:// " ;
}
$server .= $statistics -> config -> instance_address ;
$hostname = $statistics -> config -> instance_address ;
2018-12-15 10:32:47 +01:00
} elseif ( ! empty ( $statistics -> instance_address )) {
if ( ! empty ( $statistics -> instance_with_ssl )) {
2017-11-15 15:47:28 +01:00
$server = " https:// " ;
} else {
$server = " http:// " ;
}
$server .= $statistics -> instance_address ;
$hostname = $statistics -> instance_address ;
}
2018-07-10 14:27:56 +02:00
if ( ! empty ( $statistics -> users )) {
2017-11-15 15:47:28 +01:00
foreach ( $statistics -> users as $nick => $user ) {
$profile_url = $server . " / " . $user -> nickname ;
2018-01-15 14:05:12 +01:00
$contact = [ " url " => $profile_url ,
2017-11-15 15:47:28 +01:00
" name " => $user -> fullname ,
" addr " => $user -> nickname . " @ " . $hostname ,
" nick " => $user -> nickname ,
2018-08-11 22:40:44 +02:00
" network " => Protocol :: OSTATUS ,
2018-10-23 16:36:57 +02:00
" photo " => System :: baseUrl () . " /images/person-300.jpg " ];
2018-07-10 14:27:56 +02:00
if ( isset ( $user -> bio )) {
$contact [ " about " ] = $user -> bio ;
}
2017-11-15 15:47:28 +01:00
self :: getId ( $contact );
}
}
}
/**
* @ brief Asking GNU Social server on a regular base for their user data
2017-11-19 23:04:40 +01:00
* @ return void
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-11-15 15:47:28 +01:00
*/
2017-11-15 18:02:08 +01:00
public static function discoverGsUsers ()
2017-11-15 15:47:28 +01:00
{
$requery_days = intval ( Config :: get ( " system " , " poco_requery_days " ));
$last_update = date ( " c " , time () - ( 60 * 60 * 24 * $requery_days ));
2019-05-18 22:17:57 +02:00
$r = DBA :: select ( 'gserver' , [ 'nurl' , 'url' ], [
' `network` = ?
AND `last_contact` >= `last_failure`
AND `last_poco_query` < ? ' ,
Protocol :: OSTATUS ,
$last_update
], [
'limit' => 5 ,
'order' => [ 'RAND()' ]
]);
2017-11-15 15:47:28 +01:00
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2017-11-15 15:47:28 +01:00
return ;
}
foreach ( $r as $server ) {
2017-11-15 18:02:08 +01:00
self :: fetchGsUsers ( $server [ " url " ]);
2018-07-21 15:10:13 +02:00
q ( " UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s' " , DBA :: escape ( DateTimeFormat :: utcNow ()), DBA :: escape ( $server [ " nurl " ]));
2017-11-15 15:47:28 +01:00
}
}
2017-11-19 23:03:39 +01:00
2017-11-20 17:14:35 +01:00
/**
2019-05-18 22:17:57 +02:00
* Returns a random , global contact of the current node
*
* @ return string The profile URL
2019-01-07 16:24:06 +01:00
* @ throws Exception
2017-11-20 17:14:35 +01:00
*/
public static function getRandomUrl ()
{
2019-05-18 22:17:57 +02:00
$r = DBA :: selectFirst ( 'gcontact' , [ 'url' ], [
' `network` = ?
AND `last_contact` >= `last_failure`
2019-05-19 03:12:22 +02:00
AND `updated` > ? ' ,
2019-05-18 22:17:57 +02:00
Protocol :: DFRN ,
DateTimeFormat :: utc ( 'now - 1 month' ),
], [ 'order' => [ 'RAND()' ]]);
2017-11-19 23:03:39 +01:00
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2019-05-18 22:17:57 +02:00
return $r [ 'url' ];
2017-11-20 17:14:35 +01:00
}
2017-11-19 23:03:39 +01:00
return '' ;
}
2017-11-15 15:47:28 +01:00
}