2011-10-27 05:09:03 +02:00
< ? php
2020-02-09 16:34:23 +01:00
/**
* @ copyright Copyright ( C ) 2020 , Friendica
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
* @ see https :// web . archive . org / web / 20160405005550 / http :// portablecontacts . net / draft - spec . html
*/
2018-01-27 03:38:34 +01:00
2017-04-30 06:07:00 +02:00
use Friendica\App ;
2018-02-15 03:33:55 +01:00
use Friendica\Content\Text\BBCode ;
2018-10-29 22:20:46 +01:00
use Friendica\Core\Logger ;
2018-08-11 22:40:44 +02:00
use Friendica\Core\Protocol ;
2018-10-31 15:35:50 +01:00
use Friendica\Core\Renderer ;
2018-07-21 14:40:21 +02:00
use Friendica\Database\DBA ;
2020-01-07 00:41:20 +01:00
use Friendica\DI ;
2017-11-15 16:53:16 +01:00
use Friendica\Protocol\PortableContact ;
2018-01-27 03:38:34 +01:00
use Friendica\Util\DateTimeFormat ;
2018-11-08 16:14:37 +01:00
use Friendica\Util\Strings ;
2018-11-05 13:40:18 +01:00
use Friendica\Util\XML ;
2017-04-30 06:07:00 +02:00
2017-01-09 13:14:25 +01:00
function poco_init ( App $a ) {
2011-11-29 00:11:59 +01:00
$system_mode = false ;
2020-01-19 21:21:13 +01:00
if ( intval ( DI :: config () -> get ( 'system' , 'block_public' )) || ( DI :: config () -> get ( 'system' , 'block_local_dir' ))) {
2019-05-02 05:16:10 +02:00
throw new \Friendica\Network\HTTPException\ForbiddenException ();
2017-03-13 17:18:45 +01:00
}
2011-11-29 00:11:59 +01:00
2017-03-13 17:18:45 +01:00
if ( $a -> argc > 1 ) {
2019-01-07 18:51:48 +01:00
$nickname = Strings :: escapeTags ( trim ( $a -> argv [ 1 ]));
2011-10-27 05:09:03 +02:00
}
2019-01-07 18:51:48 +01:00
if ( empty ( $nickname )) {
2020-02-17 08:16:45 +01:00
if ( ! DBA :: exists ( 'profile' , [ 'net-publish' => true ])) {
2019-05-02 05:16:10 +02:00
throw new \Friendica\Network\HTTPException\ForbiddenException ();
2016-12-20 15:37:27 +01:00
}
2011-11-29 00:11:59 +01:00
$system_mode = true ;
}
2011-10-27 09:57:19 +02:00
2019-10-15 15:01:17 +02:00
$format = ( $_GET [ 'format' ] ? ? '' ) ? : 'json' ;
2011-10-27 05:09:03 +02:00
$justme = false ;
2015-01-27 08:01:37 +01:00
$global = false ;
2011-10-27 05:09:03 +02:00
2017-03-13 15:57:11 +01:00
if ( $a -> argc > 1 && $a -> argv [ 1 ] === '@server' ) {
// List of all servers that this server knows
2017-11-15 16:53:16 +01:00
$ret = PortableContact :: serverlist ();
2017-03-04 12:04:00 +01:00
header ( 'Content-type: application/json' );
echo json_encode ( $ret );
2018-12-26 06:40:12 +01:00
exit ();
2017-03-04 12:04:00 +01:00
}
2017-01-26 15:23:30 +01:00
2017-03-13 15:57:11 +01:00
if ( $a -> argc > 1 && $a -> argv [ 1 ] === '@global' ) {
2018-05-13 10:06:43 +02:00
// List of all profiles that this server recently had data from
2015-01-27 08:01:37 +01:00
$global = true ;
2018-01-27 03:38:34 +01:00
$update_limit = date ( DateTimeFormat :: MYSQL , time () - 30 * 86400 );
2015-01-27 08:01:37 +01:00
}
2017-03-13 15:57:11 +01:00
if ( $a -> argc > 2 && $a -> argv [ 2 ] === '@me' ) {
2011-10-27 05:09:03 +02:00
$justme = true ;
2017-03-13 15:57:11 +01:00
}
if ( $a -> argc > 3 && $a -> argv [ 3 ] === '@all' ) {
2011-10-27 05:09:03 +02:00
$justme = false ;
2017-03-13 15:57:11 +01:00
}
if ( $a -> argc > 3 && $a -> argv [ 3 ] === '@self' ) {
2011-10-27 05:09:03 +02:00
$justme = true ;
2017-03-13 15:57:11 +01:00
}
2017-03-13 17:18:45 +01:00
if ( $a -> argc > 4 && intval ( $a -> argv [ 4 ]) && $justme == false ) {
2011-10-27 05:09:03 +02:00
$cid = intval ( $a -> argv [ 4 ]);
2017-03-13 15:57:11 +01:00
}
2011-10-27 05:09:03 +02:00
2020-02-20 18:24:41 +01:00
if ( ! $system_mode && ! $global ) {
2020-04-24 13:55:46 +02:00
$user = DBA :: selectFirst ( 'owner-view' , [ 'uid' , 'nickname' ], [ 'nickname' => $nickname , 'hide-friends' => false ]);
2020-02-20 18:24:41 +01:00
if ( ! DBA :: isResult ( $user )) {
2019-05-02 05:16:10 +02:00
throw new \Friendica\Network\HTTPException\NotFoundException ();
2017-03-13 17:18:45 +01:00
}
2011-11-29 00:11:59 +01:00
}
2011-10-27 05:09:03 +02:00
2017-03-13 17:18:45 +01:00
if ( $justme ) {
2015-01-05 22:44:55 +01:00
$sql_extra = " AND `contact`.`self` = 1 " ;
2018-07-08 14:58:43 +02:00
} else {
$sql_extra = " " ;
2017-03-13 17:18:45 +01:00
}
2011-10-27 05:09:03 +02:00
2018-07-08 14:58:43 +02:00
if ( ! empty ( $cid )) {
2017-05-09 03:55:04 +02:00
$sql_extra = sprintf ( " AND `contact`.`id` = %d " , intval ( $cid ));
2017-03-13 17:18:45 +01:00
}
2018-11-30 15:06:22 +01:00
if ( ! empty ( $_GET [ 'updatedSince' ])) {
2018-01-27 03:38:34 +01:00
$update_limit = date ( DateTimeFormat :: MYSQL , strtotime ( $_GET [ 'updatedSince' ]));
2017-03-13 17:18:45 +01:00
}
2015-01-27 08:01:37 +01:00
if ( $global ) {
2017-05-09 03:55:04 +02:00
$contacts = q ( " SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `updated` >= `last_failure` AND NOT `hide` AND `network` IN ('%s', '%s', '%s') " ,
2018-07-21 15:10:13 +02:00
DBA :: escape ( $update_limit ),
2018-08-11 22:40:44 +02:00
DBA :: escape ( Protocol :: DFRN ),
DBA :: escape ( Protocol :: DIASPORA ),
DBA :: escape ( Protocol :: OSTATUS )
2015-01-27 08:01:37 +01:00
);
2017-03-13 17:18:45 +01:00
} elseif ( $system_mode ) {
2020-02-17 08:16:45 +01:00
$totalResults = DBA :: count ( 'profile' , [ 'net-publish' => true ]);
2015-01-27 08:01:37 +01:00
} else {
2017-05-09 03:55:04 +02:00
$contacts = q ( " SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
2020-07-19 13:42:23 +02:00
AND NOT `failed`
2015-07-25 10:50:00 +02:00
AND `network` IN ( '%s' , '%s' , '%s' , '%s' ) $sql_extra " ,
2015-01-05 22:44:55 +01:00
intval ( $user [ 'uid' ]),
2018-08-11 22:40:44 +02:00
DBA :: escape ( Protocol :: DFRN ),
DBA :: escape ( Protocol :: DIASPORA ),
DBA :: escape ( Protocol :: OSTATUS ),
DBA :: escape ( Protocol :: STATUSNET )
2011-11-29 00:11:59 +01:00
);
}
2020-02-17 08:16:45 +01:00
if ( empty ( $totalResults ) && DBA :: isResult ( $contacts )) {
2017-05-09 03:55:04 +02:00
$totalResults = intval ( $contacts [ 0 ][ 'total' ]);
2020-02-17 08:16:45 +01:00
} elseif ( empty ( $totalResults )) {
2011-10-27 05:09:03 +02:00
$totalResults = 0 ;
2017-03-13 17:18:45 +01:00
}
2018-07-08 14:58:43 +02:00
if ( ! empty ( $_GET [ 'startIndex' ])) {
$startIndex = intval ( $_GET [ 'startIndex' ]);
} else {
2011-10-27 05:09:03 +02:00
$startIndex = 0 ;
2017-03-13 17:18:45 +01:00
}
2018-11-30 15:06:22 +01:00
$itemsPerPage = (( ! empty ( $_GET [ 'count' ])) ? intval ( $_GET [ 'count' ]) : $totalResults );
2011-10-27 05:09:03 +02:00
2015-01-27 08:01:37 +01:00
if ( $global ) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Start global query " , Logger :: DEBUG );
2017-05-09 03:55:04 +02:00
$contacts = q ( " SELECT * FROM `gcontact` WHERE `updated` > '%s' AND NOT `hide` AND `network` IN ('%s', '%s', '%s') AND `updated` > `last_failure`
2015-07-25 12:05:27 +02:00
ORDER BY `updated` DESC LIMIT % d , % d " ,
2018-07-21 15:10:13 +02:00
DBA :: escape ( $update_limit ),
2018-08-11 22:40:44 +02:00
DBA :: escape ( Protocol :: DFRN ),
DBA :: escape ( Protocol :: DIASPORA ),
DBA :: escape ( Protocol :: OSTATUS ),
2015-01-27 08:01:37 +01:00
intval ( $startIndex ),
intval ( $itemsPerPage )
);
2017-03-13 17:18:45 +01:00
} elseif ( $system_mode ) {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Start system mode query " , Logger :: DEBUG );
2020-04-24 15:41:11 +02:00
$contacts = DBA :: selectToArray ( 'owner-view' , [], [ 'net-publish' => true ], [ 'limit' => [ $startIndex , $itemsPerPage ]]);
2015-01-27 08:01:37 +01:00
} else {
2018-10-30 14:58:45 +01:00
Logger :: log ( " Start query for user " . $user [ 'nickname' ], Logger :: DEBUG );
2017-05-09 03:55:04 +02:00
$contacts = q ( " SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
2020-07-19 13:42:23 +02:00
AND NOT `failed`
2015-07-25 10:50:00 +02:00
AND `network` IN ( '%s' , '%s' , '%s' , '%s' ) $sql_extra LIMIT % d , % d " ,
2011-11-29 00:11:59 +01:00
intval ( $user [ 'uid' ]),
2018-08-11 22:40:44 +02:00
DBA :: escape ( Protocol :: DFRN ),
DBA :: escape ( Protocol :: DIASPORA ),
DBA :: escape ( Protocol :: OSTATUS ),
DBA :: escape ( Protocol :: STATUSNET ),
2011-11-29 00:11:59 +01:00
intval ( $startIndex ),
intval ( $itemsPerPage )
);
}
2018-10-30 14:58:45 +01:00
Logger :: log ( " Query done " , Logger :: DEBUG );
2015-01-27 08:01:37 +01:00
2018-01-15 14:05:12 +01:00
$ret = [];
2018-11-30 15:06:22 +01:00
if ( ! empty ( $_GET [ 'sorted' ])) {
2015-01-27 08:01:37 +01:00
$ret [ 'sorted' ] = false ;
2017-03-13 17:18:45 +01:00
}
2018-11-30 15:06:22 +01:00
if ( ! empty ( $_GET [ 'filtered' ])) {
2015-01-27 08:01:37 +01:00
$ret [ 'filtered' ] = false ;
2017-03-13 17:18:45 +01:00
}
2018-11-30 15:06:22 +01:00
if ( ! empty ( $_GET [ 'updatedSince' ]) && ! $global ) {
2015-01-27 08:01:37 +01:00
$ret [ 'updatedSince' ] = false ;
2017-03-13 17:18:45 +01:00
}
2015-02-15 10:52:45 +01:00
$ret [ 'startIndex' ] = ( int ) $startIndex ;
$ret [ 'itemsPerPage' ] = ( int ) $itemsPerPage ;
$ret [ 'totalResults' ] = ( int ) $totalResults ;
2018-01-15 14:05:12 +01:00
$ret [ 'entry' ] = [];
2011-10-27 09:57:19 +02:00
2017-03-21 17:02:59 +01:00
2018-01-15 14:05:12 +01:00
$fields_ret = [
2017-03-21 17:02:59 +01:00
'id' => false ,
'displayName' => false ,
'urls' => false ,
'updated' => false ,
2011-10-27 13:38:33 +02:00
'preferredUsername' => false ,
2017-03-21 17:02:59 +01:00
'photos' => false ,
'aboutMe' => false ,
'currentLocation' => false ,
'network' => false ,
'tags' => false ,
'address' => false ,
'contactType' => false ,
'generation' => false
2018-01-15 14:05:12 +01:00
];
2011-10-27 05:09:03 +02:00
2018-11-30 15:06:22 +01:00
if ( empty ( $_GET [ 'fields' ]) || ( $_GET [ 'fields' ] === '@all' )) {
2017-03-13 17:18:45 +01:00
foreach ( $fields_ret as $k => $v ) {
2011-10-27 10:54:52 +02:00
$fields_ret [ $k ] = true ;
2017-03-13 17:18:45 +01:00
}
} else {
2017-05-09 03:55:04 +02:00
$fields_req = explode ( ',' , $_GET [ 'fields' ]);
2017-03-13 17:18:45 +01:00
foreach ( $fields_req as $f ) {
2011-10-27 09:57:19 +02:00
$fields_ret [ trim ( $f )] = true ;
2017-03-13 17:18:45 +01:00
}
2011-10-27 09:57:19 +02:00
}
2020-06-09 00:58:05 +02:00
if ( ! is_array ( $contacts )) {
throw new \Friendica\Network\HTTPException\InternalServerErrorException ();
}
2018-07-08 14:58:43 +02:00
2020-06-09 00:58:05 +02:00
if ( DBA :: isResult ( $contacts )) {
foreach ( $contacts as $contact ) {
if ( ! isset ( $contact [ 'updated' ])) {
$contact [ 'updated' ] = '' ;
}
2015-02-15 10:52:45 +01:00
2020-06-09 00:58:05 +02:00
if ( ! isset ( $contact [ 'generation' ])) {
if ( $global ) {
$contact [ 'generation' ] = 3 ;
} elseif ( $system_mode ) {
$contact [ 'generation' ] = 1 ;
} else {
$contact [ 'generation' ] = 2 ;
2015-07-25 12:05:27 +02:00
}
2020-06-09 00:58:05 +02:00
}
2015-07-25 12:05:27 +02:00
2020-06-09 00:58:05 +02:00
if (( $contact [ 'keywords' ] == " " ) && isset ( $contact [ 'pub_keywords' ])) {
$contact [ 'keywords' ] = $contact [ 'pub_keywords' ];
}
if ( isset ( $contact [ 'account-type' ])) {
$contact [ 'contact-type' ] = $contact [ 'account-type' ];
}
$about = DI :: cache () -> get ( " about: " . $contact [ 'updated' ] . " : " . $contact [ 'nurl' ]);
if ( is_null ( $about )) {
$about = BBCode :: convert ( $contact [ 'about' ], false );
DI :: cache () -> set ( " about: " . $contact [ 'updated' ] . " : " . $contact [ 'nurl' ], $about );
}
2015-10-06 06:56:31 +02:00
2020-06-09 00:58:05 +02:00
// Non connected persons can only see the keywords of a Diaspora account
if ( $contact [ 'network' ] == Protocol :: DIASPORA ) {
$contact [ 'location' ] = " " ;
$about = " " ;
}
$entry = [];
if ( $fields_ret [ 'id' ]) {
$entry [ 'id' ] = ( int ) $contact [ 'id' ];
}
if ( $fields_ret [ 'displayName' ]) {
$entry [ 'displayName' ] = $contact [ 'name' ];
}
if ( $fields_ret [ 'aboutMe' ]) {
$entry [ 'aboutMe' ] = $about ;
}
if ( $fields_ret [ 'currentLocation' ]) {
$entry [ 'currentLocation' ] = $contact [ 'location' ];
}
if ( $fields_ret [ 'generation' ]) {
$entry [ 'generation' ] = ( int ) $contact [ 'generation' ];
}
if ( $fields_ret [ 'urls' ]) {
$entry [ 'urls' ] = [[ 'value' => $contact [ 'url' ], 'type' => 'profile' ]];
if ( $contact [ 'addr' ] && ( $contact [ 'network' ] !== Protocol :: MAIL )) {
$entry [ 'urls' ][] = [ 'value' => 'acct:' . $contact [ 'addr' ], 'type' => 'webfinger' ];
2017-03-13 17:18:45 +01:00
}
2020-06-09 00:58:05 +02:00
}
if ( $fields_ret [ 'preferredUsername' ]) {
$entry [ 'preferredUsername' ] = $contact [ 'nick' ];
}
if ( $fields_ret [ 'updated' ]) {
if ( ! $global ) {
$entry [ 'updated' ] = $contact [ 'success_update' ];
2015-01-04 19:19:47 +01:00
2020-06-09 00:58:05 +02:00
if ( $contact [ 'name-date' ] > $entry [ 'updated' ]) {
$entry [ 'updated' ] = $contact [ 'name-date' ];
2017-03-13 17:18:45 +01:00
}
2020-06-09 00:58:05 +02:00
if ( $contact [ 'uri-date' ] > $entry [ 'updated' ]) {
$entry [ 'updated' ] = $contact [ 'uri-date' ];
2017-03-13 17:18:45 +01:00
}
2020-06-09 00:58:05 +02:00
if ( $contact [ 'avatar-date' ] > $entry [ 'updated' ]) {
$entry [ 'updated' ] = $contact [ 'avatar-date' ];
2017-03-13 17:18:45 +01:00
}
2020-06-09 00:58:05 +02:00
} else {
$entry [ 'updated' ] = $contact [ 'updated' ];
}
$entry [ 'updated' ] = date ( " c " , strtotime ( $entry [ 'updated' ]));
}
if ( $fields_ret [ 'photos' ]) {
$entry [ 'photos' ] = [[ 'value' => $contact [ 'photo' ], 'type' => 'profile' ]];
}
if ( $fields_ret [ 'network' ]) {
$entry [ 'network' ] = $contact [ 'network' ];
if ( $entry [ 'network' ] == Protocol :: STATUSNET ) {
$entry [ 'network' ] = Protocol :: OSTATUS ;
2015-01-08 07:59:20 +01:00
}
2020-06-09 00:58:05 +02:00
if (( $entry [ 'network' ] == " " ) && ( $contact [ 'self' ])) {
$entry [ 'network' ] = Protocol :: DFRN ;
}
}
if ( $fields_ret [ 'tags' ]) {
$tags = str_replace ( " , " , " " , $contact [ 'keywords' ]);
$tags = explode ( " " , $tags );
2015-01-25 13:19:37 +01:00
2020-06-09 00:58:05 +02:00
$cleaned = [];
foreach ( $tags as $tag ) {
$tag = trim ( strtolower ( $tag ));
if ( $tag != " " ) {
$cleaned [] = $tag ;
2015-01-25 13:19:37 +01:00
}
}
2015-02-04 10:43:30 +01:00
2020-06-09 00:58:05 +02:00
$entry [ 'tags' ] = [ $cleaned ];
}
if ( $fields_ret [ 'address' ]) {
$entry [ 'address' ] = [];
2015-02-04 10:43:30 +01:00
2020-06-09 00:58:05 +02:00
// Deactivated. It just reveals too much data. (Although its from the default profile)
//if (isset($rr['address']))
// $entry['address']['streetAddress'] = $rr['address'];
2015-02-04 10:43:30 +01:00
2020-06-09 00:58:05 +02:00
if ( isset ( $contact [ 'locality' ])) {
$entry [ 'address' ][ 'locality' ] = $contact [ 'locality' ];
}
if ( isset ( $contact [ 'region' ])) {
$entry [ 'address' ][ 'region' ] = $contact [ 'region' ];
2015-02-04 10:43:30 +01:00
}
2020-06-09 00:58:05 +02:00
// See above
//if (isset($rr['postal-code']))
// $entry['address']['postalCode'] = $rr['postal-code'];
2015-01-25 13:19:37 +01:00
2020-06-09 00:58:05 +02:00
if ( isset ( $contact [ 'country' ])) {
$entry [ 'address' ][ 'country' ] = $contact [ 'country' ];
2017-03-13 17:18:45 +01:00
}
2011-10-27 09:57:19 +02:00
}
2020-06-09 00:58:05 +02:00
if ( $fields_ret [ 'contactType' ]) {
$entry [ 'contactType' ] = intval ( $contact [ 'contact-type' ]);
}
$ret [ 'entry' ][] = $entry ;
2017-03-13 17:18:45 +01:00
}
} else {
2020-06-09 00:58:05 +02:00
$ret [ 'entry' ][] = [];
2017-03-13 17:18:45 +01:00
}
2019-05-02 05:16:10 +02:00
2018-10-30 14:58:45 +01:00
Logger :: log ( " End of poco " , Logger :: DEBUG );
2015-07-25 12:05:27 +02:00
2017-03-13 17:18:45 +01:00
if ( $format === 'xml' ) {
2011-10-27 09:57:19 +02:00
header ( 'Content-type: text/xml' );
2018-11-05 13:40:18 +01:00
echo Renderer :: replaceMacros ( Renderer :: getMarkupTemplate ( 'poco_xml.tpl' ), XML :: arrayEscape ([ '$response' => $ret ]));
2018-12-26 06:40:12 +01:00
exit ();
2011-10-27 09:57:19 +02:00
}
2017-03-13 17:18:45 +01:00
if ( $format === 'json' ) {
2011-10-27 09:57:19 +02:00
header ( 'Content-type: application/json' );
echo json_encode ( $ret );
2018-12-26 06:40:12 +01:00
exit ();
2017-03-13 17:18:45 +01:00
} else {
2019-05-02 05:16:10 +02:00
throw new \Friendica\Network\HTTPException\InternalServerErrorException ();
2017-03-13 17:18:45 +01:00
}
2012-12-22 20:57:29 +01:00
}