Merge pull request #11670 from Quix0r/fixes/more-type-hints-004

Fixes/more type hints 004
This commit is contained in:
Hypolite Petovan 2022-06-22 10:49:20 -04:00 committed by GitHub
commit b96daeeeef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1734 additions and 1548 deletions

View file

@ -102,6 +102,7 @@ abstract class BaseModule implements ICanHandleRequests
* e.g. from protocol implementations.
*
* @param string[] $request The $_REQUEST content
* @return void
*/
protected function rawContent(array $request = [])
{
@ -117,6 +118,7 @@ abstract class BaseModule implements ICanHandleRequests
* XML feed or a JSON output.
*
* @param string[] $request The $_REQUEST content
* @return string
*/
protected function content(array $request = []): string
{
@ -130,6 +132,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content
*
* @param string[] $request The $_REQUEST content
* @return void
*/
protected function delete(array $request = [])
{
@ -142,6 +145,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content
*
* @param string[] $request The $_REQUEST content
* @return void
*/
protected function patch(array $request = [])
{
@ -154,7 +158,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content
*
* @param string[] $request The $_REQUEST content
*
* @return void
*/
protected function post(array $request = [])
{
@ -168,6 +172,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content
*
* @param string[] $request The $_REQUEST content
* @return void
*/
protected function put(array $request = [])
{
@ -279,12 +284,12 @@ abstract class BaseModule implements ICanHandleRequests
/**
* Fetch a request value and apply default values and check against minimal and maximal values
*
* @param array $input
* @param string $parameter
* @param mixed $default
* @param mixed $minimal_value
* @param mixed $maximum_value
* @return mixed
* @param array $input Input fields
* @param string $parameter Parameter
* @param mixed $default Default
* @param mixed $minimal_value Minimal value
* @param mixed $maximum_value Maximum value
* @return mixed null on error anything else on success (?)
*/
public function getRequestValue(array $input, string $parameter, $default = null, $minimal_value = null, $maximum_value = null)
{
@ -320,7 +325,7 @@ abstract class BaseModule implements ICanHandleRequests
return $value;
}
/*
/**
* Functions used to protect against Cross-Site Request Forgery
* The security token has to base on at least one value that an attacker can't know - here it's the session ID and the private key.
* In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes;
@ -330,8 +335,11 @@ abstract class BaseModule implements ICanHandleRequests
* If the new page contains by any chance external elements, then the used security token is exposed by the referrer.
* Actually, important actions should not be triggered by Links / GET-Requests at all, but sometimes they still are,
* so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types).
*
* @param string $typename Type name
* @return string Security hash with timestamp
*/
public static function getFormSecurityToken(string $typename = '')
public static function getFormSecurityToken(string $typename = ''): string
{
$user = User::getById(DI::app()->getLoggedInUserId(), ['guid', 'prvkey']);
$timestamp = time();
@ -404,7 +412,7 @@ abstract class BaseModule implements ICanHandleRequests
}
}
protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab)
protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab): array
{
$tabs = [
[

View file

@ -1247,16 +1247,28 @@ class BBCode
return $text;
}
private static function expandLinksCallback($match)
/**
* Callback: Expands links from given $match array
*
* @param arrat $match Array with link match
* @return string BBCode
*/
private static function expandLinksCallback(array $match): string
{
if (($match[3] == '') || ($match[2] == $match[3]) || stristr($match[2], $match[3])) {
return ($match[1] . "[url]" . $match[2] . "[/url]");
return ($match[1] . '[url]' . $match[2] . '[/url]');
} else {
return ($match[1] . $match[3] . " [url]" . $match[2] . "[/url]");
return ($match[1] . $match[3] . ' [url]' . $match[2] . '[/url]');
}
}
private static function cleanPictureLinksCallback($match)
/**
* Callback: Cleans picture links
*
* @param arrat $match Array with link match
* @return string BBCode
*/
private static function cleanPictureLinksCallback(array $match): string
{
// When the picture link is the own photo path then we can avoid fetching the link
$own_photo_url = preg_quote(Strings::normaliseLink(DI::baseUrl()->get()) . '/photos/');
@ -1325,7 +1337,13 @@ class BBCode
return $text;
}
public static function cleanPictureLinks($text)
/**
* Cleans picture links
*
* @param string $text HTML/BBCode string
* @return string Cleaned HTML/BBCode
*/
public static function cleanPictureLinks(string $text): string
{
DI::profiler()->startRecording('rendering');
$return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img=(.*)\](.*)\[\/img\]\[\/url\]&Usi", 'self::cleanPictureLinksCallback', $text);
@ -1334,7 +1352,13 @@ class BBCode
return $return;
}
public static function removeLinks(string $bbcode)
/**
* Removes links
*
* @param string $text HTML/BBCode string
* @return string Cleaned HTML/BBCode
*/
public static function removeLinks(string $bbcode): string
{
DI::profiler()->startRecording('rendering');
$bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $1 ', $bbcode);
@ -1350,10 +1374,10 @@ class BBCode
/**
* Replace names in mentions with nicknames
*
* @param string $body
* @param string $body HTML/BBCode
* @return string Body with replaced mentions
*/
public static function setMentionsToNicknames(string $body):string
public static function setMentionsToNicknames(string $body): string
{
DI::profiler()->startRecording('rendering');
$regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
@ -1366,10 +1390,10 @@ class BBCode
* Callback function to replace a Friendica style mention in a mention with the nickname
*
* @param array $match Matching values for the callback
* @return string Replaced mention
* @return string Replaced mention or empty string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function mentionCallback($match)
private static function mentionCallback(array $match): string
{
if (empty($match[2])) {
return '';
@ -1407,7 +1431,7 @@ class BBCode
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function convertForUriId(int $uriid = null, string $text = null, int $simple_html = self::INTERNAL)
public static function convertForUriId(int $uriid = null, string $text = null, int $simple_html = self::INTERNAL): string
{
$try_oembed = ($simple_html == self::INTERNAL);
@ -1437,10 +1461,10 @@ class BBCode
* @param int $simple_html
* @param bool $for_plaintext
* @param int $uriid
* @return string
* @return string Converted code or empty string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function convert(string $text = null, $try_oembed = true, $simple_html = self::INTERNAL, $for_plaintext = false, $uriid = 0)
public static function convert(string $text = null, bool $try_oembed = true, int $simple_html = self::INTERNAL, bool $for_plaintext = false, int $uriid = 0): string
{
// Accounting for null default column values
if (is_null($text) || $text === '') {
@ -2142,7 +2166,7 @@ class BBCode
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function bbCodeMention2DiasporaCallback($match)
private static function bbCodeMention2DiasporaCallback(array $match): string
{
$contact = Contact::getByURL($match[3], false, ['addr']);
if (empty($contact['addr'])) {
@ -2164,7 +2188,7 @@ class BBCode
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function toMarkdown($text, $for_diaspora = true)
public static function toMarkdown(string $text, bool $for_diaspora = true): string
{
DI::profiler()->startRecording('rendering');
$original_text = $text;
@ -2249,7 +2273,7 @@ class BBCode
*
* @return array List of tag and person names
*/
public static function getTags($string)
public static function getTags(string $string): array
{
DI::profiler()->startRecording('rendering');
$ret = [];
@ -2309,10 +2333,10 @@ class BBCode
/**
* Expand tags to URLs, checks the tag is at the start of a line or preceded by a non-word character
*
* @param string $body
* @param string $body HTML/BBCode
* @return string body with expanded tags
*/
public static function expandTags(string $body)
public static function expandTags(string $body): string
{
return preg_replace_callback("/(?<=\W|^)([!#@])([^\^ \x0D\x0A,;:?'\"]*[^\^ \x0D\x0A,;:?!'\".])/",
function ($match) {
@ -2336,7 +2360,7 @@ class BBCode
/**
* Perform a custom function on a text after having escaped blocks enclosed in the provided tag list.
*
* @param string $text
* @param string $text HTML/BBCode
* @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre']
* @param callable $callback
* @return string
@ -2352,14 +2376,14 @@ class BBCode
/**
* Replaces mentions in the provided message body in BBCode links for the provided user and network if any
*
* @param $body
* @param $profile_uid
* @param $network
* @return string
* @param string $body HTML/BBCode
* @param int $profile_uid Profile user id
* @param string $network Network name
* @return string HTML/BBCode with inserted images
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function setMentions($body, $profile_uid = 0, $network = '')
public static function setMentions(string $body, $profile_uid = 0, $network = '')
{
DI::profiler()->startRecording('rendering');
$body = self::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) {
@ -2406,7 +2430,7 @@ class BBCode
* @return string
* @TODO Rewrite to handle over whole record array
*/
public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null)
public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null): string
{
DI::profiler()->startRecording('rendering');
$header = "[share author='" . str_replace(["'", "[", "]"], ["&#x27;", "&#x5B;", "&#x5D;"], $author) .

View file

@ -73,11 +73,12 @@ class Tag
/**
* Store tag/mention elements
*
* @param integer $uriid
* @param integer $type
* @param string $name
* @param string $url
* @param integer $target
* @param integer $uriid URI id
* @param integer $type Tag type
* @param string $name Tag name
* @param string $url Contact URL (optional)
* @param integer $target Target (default: null)
* @return void
*/
public static function store(int $uriid, int $type, string $name, string $url = '', int $target = null)
{
@ -249,13 +250,14 @@ class Tag
/**
* Store tag/mention elements
*
* @param integer $uriid
* @param string $hash
* @param string $name
* @param string $url
* @param boolean $probing
* @param integer $uriid URI id
* @param string $hash Hash
* @param string $name Name
* @param string $url URL
* @param boolean $probing Whether probing is active
* @return void
*/
public static function storeByHash(int $uriid, string $hash, string $name, string $url = '', $probing = true)
public static function storeByHash(int $uriid, string $hash, string $name, string $url = '', bool $probing = true)
{
$type = self::getTypeForHash($hash);
if ($type == self::UNKNOWN) {
@ -293,8 +295,9 @@ class Tag
* @param string $body Body of the post
* @param string $tags Accepted tags
* @param boolean $probing Perform a probing for contacts, adding them if needed
* @return void
*/
public static function storeFromBody(int $uriid, string $body, string $tags = null, $probing = true)
public static function storeFromBody(int $uriid, string $body, string $tags = null, bool $probing = true)
{
Logger::info('Check for tags', ['uri-id' => $uriid, 'hash' => $tags, 'callstack' => System::callstack()]);
@ -330,6 +333,7 @@ class Tag
*
* @param integer $uriid URI-Id
* @param string $body Body of the post
* @return void
*/
public static function storeRawTagsFromBody(int $uriid, string $body)
{
@ -364,10 +368,11 @@ class Tag
/**
* Remove tag/mention
*
* @param integer $uriid
* @param integer $type
* @param string $name
* @param string $url
* @param integer $uriid URI id
* @param integer $type Type
* @param string $name Name
* @param string $url URL
* @return void
*/
public static function remove(int $uriid, int $type, string $name, string $url = '')
{

View file

@ -43,7 +43,10 @@ require_once 'boot.php';
abstract class BaseAdmin extends BaseModule
{
/**
* Checks admin access and throws exceptions if not logged-in administrator
*
* @param bool $interactive
* @return void
* @throws HTTPException\ForbiddenException
* @throws HTTPException\InternalServerErrorException
*/

View file

@ -34,7 +34,7 @@ abstract class HTTPException extends Exception
protected $httpdesc = '';
protected $explanation = '';
public function __construct($message = '', Exception $previous = null)
public function __construct(string $message = '', Exception $previous = null)
{
parent::__construct($message, $this->code, $previous);
}

View file

@ -85,18 +85,17 @@ class Probe
* Rearrange the array so that it always has the same order
*
* @param array $data Unordered data
*
* @return array Ordered data
*/
private static function rearrangeData($data)
private static function rearrangeData(array $data): array
{
$fields = ["name", "nick", "guid", "url", "addr", "alias", "photo", "header",
"account-type", "community", "keywords", "location", "about", "xmpp", "matrix",
"hide", "batch", "notify", "poll", "request", "confirm", "subscribe", "poco",
"following", "followers", "inbox", "outbox", "sharedinbox",
"priority", "network", "pubkey", "manually-approve", "baseurl", "gsid"];
$fields = ['name', 'nick', 'guid', 'url', 'addr', 'alias', 'photo', 'header',
'account-type', 'community', 'keywords', 'location', 'about', 'xmpp', 'matrix',
'hide', 'batch', 'notify', 'poll', 'request', 'confirm', 'subscribe', 'poco',
'following', 'followers', 'inbox', 'outbox', 'sharedinbox',
'priority', 'network', 'pubkey', 'manually-approve', 'baseurl', 'gsid'];
$numeric_fields = ["gsid", "hide", "account-type", "manually-approve"];
$numeric_fields = ['gsid', 'hide', 'account-type', 'manually-approve'];
$newdata = [];
foreach ($fields as $field) {
@ -107,14 +106,14 @@ class Probe
$newdata[$field] = $data[$field];
}
} elseif (!in_array($field, $numeric_fields)) {
$newdata[$field] = "";
$newdata[$field] = '';
} else {
$newdata[$field] = null;
}
}
// We don't use the "priority" field anymore and replace it with a dummy.
$newdata["priority"] = 0;
$newdata['priority'] = 0;
return $newdata;
}
@ -123,10 +122,9 @@ class Probe
* Check if the hostname belongs to the own server
*
* @param string $host The hostname that is to be checked
*
* @return bool Does the testes hostname belongs to the own server?
*/
private static function ownHost($host)
private static function ownHost(string $host): bool
{
$own_host = DI::baseUrl()->getHostname();
@ -149,21 +147,20 @@ class Probe
* It seems as if it was dropped from the standard.
*
* @param string $host The host part of an url
*
* @return array with template and type of the webfinger template for JSON or XML
* @throws HTTPException\InternalServerErrorException
*/
private static function hostMeta($host)
private static function hostMeta(string $host): array
{
// Reset the static variable
self::$baseurl = '';
// Handles the case when the hostname contains the scheme
if (!parse_url($host, PHP_URL_SCHEME)) {
$ssl_url = "https://" . $host . "/.well-known/host-meta";
$url = "http://" . $host . "/.well-known/host-meta";
$ssl_url = 'https://' . $host . '/.well-known/host-meta';
$url = 'http://' . $host . '/.well-known/host-meta';
} else {
$ssl_url = $host . "/.well-known/host-meta";
$ssl_url = $host . '/.well-known/host-meta';
$url = '';
}
@ -210,26 +207,26 @@ class Probe
}
$links = XML::elementToArray($xrd);
if (!isset($links["xrd"]["link"])) {
if (!isset($links['xrd']['link'])) {
Logger::info('No xrd data found', ['host' => $host]);
return [];
}
$lrdd = [];
foreach ($links["xrd"]["link"] as $value => $link) {
if (!empty($link["@attributes"])) {
$attributes = $link["@attributes"];
} elseif ($value == "@attributes") {
foreach ($links['xrd']['link'] as $value => $link) {
if (!empty($link['@attributes'])) {
$attributes = $link['@attributes'];
} elseif ($value == '@attributes') {
$attributes = $link;
} else {
continue;
}
if (!empty($attributes["rel"]) && $attributes["rel"] == "lrdd" && !empty($attributes["template"])) {
$type = (empty($attributes["type"]) ? '' : $attributes["type"]);
if (!empty($attributes['rel']) && $attributes['rel'] == 'lrdd' && !empty($attributes['template'])) {
$type = (empty($attributes['type']) ? '' : $attributes['type']);
$lrdd[$type] = $attributes["template"];
$lrdd[$type] = $attributes['template'];
}
}
@ -249,11 +246,10 @@ class Probe
* Check an URI for LRDD data
*
* @param string $uri Address that should be probed
*
* @return array uri data
* @throws HTTPException\InternalServerErrorException
*/
public static function lrdd(string $uri)
public static function lrdd(string $uri): array
{
$data = self::getWebfingerArray($uri);
if (empty($data)) {
@ -261,22 +257,25 @@ class Probe
}
$webfinger = $data['webfinger'];
if (empty($webfinger["links"])) {
if (empty($webfinger['links'])) {
Logger::info('No webfinger links found', ['uri' => $uri]);
return [];
}
$data = [];
foreach ($webfinger["links"] as $link) {
$data[] = ["@attributes" => $link];
foreach ($webfinger['links'] as $link) {
$data[] = ['@attributes' => $link];
}
if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
foreach ($webfinger["aliases"] as $alias) {
$data[] = ["@attributes" =>
["rel" => "alias",
"href" => $alias]];
if (!empty($webfinger['aliases']) && is_array($webfinger['aliases'])) {
foreach ($webfinger['aliases'] as $alias) {
$data[] = [
'@attributes' => [
'rel' => 'alias',
'href' => $alias,
]
];
}
}
@ -383,10 +382,9 @@ class Probe
* Fetches the "hide" status from the profile
*
* @param string $url URL of the profile
*
* @return boolean "hide" status
*/
private static function getHideStatus($url)
private static function getHideStatus(string $url): bool
{
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::CONTENT_LENGTH => 1000000]);
if (!$curlResult->isSuccess()) {
@ -443,11 +441,11 @@ class Probe
/**
* Fetch the "subscribe" and add it to the result
*
* @param array $result
* @param array $webfinger
* @return array result
* @param array $result Result array
* @param array $webfinger Webfinger data
* @return array result Altered/unaltered result array
*/
private static function getSubscribeLink(array $result, array $webfinger)
private static function getSubscribeLink(array $result, array $webfinger): array
{
if (empty($webfinger['links'])) {
return $result;
@ -465,8 +463,8 @@ class Probe
/**
* Get webfinger data from a given URI
*
* @param string $uri
* @return array
* @param string $uri URI
* @return array Webfinger data
* @throws HTTPException\InternalServerErrorException
*/
private static function getWebfingerArray(string $uri): array
@ -583,7 +581,7 @@ class Probe
* @param string $addr
* @return array webfinger results
*/
private static function getWebfinger(string $template, string $type, string $uri, string $addr)
private static function getWebfinger(string $template, string $type, string $uri, string $addr): array
{
if (Network::isUrlBlocked($template)) {
Logger::info('Domain is blocked', ['url' => $template]);
@ -593,7 +591,7 @@ class Probe
// First try the address because this is the primary purpose of webfinger
if (!empty($addr)) {
$detected = $addr;
$path = str_replace('{uri}', urlencode("acct:" . $addr), $template);
$path = str_replace('{uri}', urlencode('acct:' . $addr), $template);
$webfinger = self::webfinger($path, $type);
if (self::$istimeout) {
return [];
@ -626,11 +624,10 @@ class Probe
* @param string $network Test for this specific network
* @param integer $uid User ID for the probe (only used for mails)
* @param array $ap_profile Previously probed AP profile
*
* @return array uri data
* @return array URI data
* @throws HTTPException\InternalServerErrorException
*/
private static function detect(string $uri, string $network, int $uid, array $ap_profile)
private static function detect(string $uri, string $network, int $uid, array $ap_profile): array
{
$hookData = [
'uri' => $uri,
@ -700,7 +697,7 @@ class Probe
if (in_array($network, ['', Protocol::ZOT])) {
$result = self::zot($webfinger, $result, $baseurl);
}
if ((!$result && ($network == "")) || ($network == Protocol::PUMPIO)) {
if ((!$result && ($network == '')) || ($network == Protocol::PUMPIO)) {
$result = self::pumpio($webfinger, $addr);
}
if (empty($result['network']) && empty($ap_profile['network']) || ($network == Protocol::FEED)) {
@ -708,30 +705,30 @@ class Probe
} else {
// We overwrite the detected nick with our try if the previois routines hadn't detected it.
// Additionally it is overwritten when the nickname doesn't make sense (contains spaces).
if ((empty($result["nick"]) || (strstr($result["nick"], " "))) && ($nick != "")) {
$result["nick"] = $nick;
if ((empty($result['nick']) || (strstr($result['nick'], ' '))) && ($nick != '')) {
$result['nick'] = $nick;
}
if (empty($result["addr"]) && ($addr != "")) {
$result["addr"] = $addr;
if (empty($result['addr']) && ($addr != '')) {
$result['addr'] = $addr;
}
}
$result = self::getSubscribeLink($result, $webfinger);
if (empty($result["network"])) {
$result["network"] = Protocol::PHANTOM;
if (empty($result['network'])) {
$result['network'] = Protocol::PHANTOM;
}
if (empty($result['baseurl']) && !empty($baseurl)) {
$result['baseurl'] = $baseurl;
}
if (empty($result["url"])) {
$result["url"] = $uri;
if (empty($result['url'])) {
$result['url'] = $uri;
}
Logger::info('Probing done', ['uri' => $uri, 'network' => $result["network"]]);
Logger::info('Probing done', ['uri' => $uri, 'network' => $result['network']]);
return $result;
}
@ -739,24 +736,24 @@ class Probe
/**
* Check for Zot contact
*
* @param array $webfinger Webfinger data
* @param array $data previously probed data
*
* @param array $webfinger Webfinger data
* @param array $data previously probed data
* @param string $baseUrl Base URL
* @return array Zot data
* @throws HTTPException\InternalServerErrorException
*/
private static function zot($webfinger, $data, $baseurl)
private static function zot(array $webfinger, array $data, string $baseurl): array
{
if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
foreach ($webfinger["aliases"] as $alias) {
if (!empty($webfinger['aliases']) && is_array($webfinger['aliases'])) {
foreach ($webfinger['aliases'] as $alias) {
if (substr($alias, 0, 5) == 'acct:') {
$data["addr"] = substr($alias, 5);
$data['addr'] = substr($alias, 5);
}
}
}
if (!empty($webfinger["subject"]) && (substr($webfinger["subject"], 0, 5) == "acct:")) {
$data["addr"] = substr($webfinger["subject"], 5);
if (!empty($webfinger['subject']) && (substr($webfinger['subject'], 0, 5) == 'acct:')) {
$data['addr'] = substr($webfinger['subject'], 5);
}
$zot_url = '';
@ -882,11 +879,10 @@ class Probe
*
* @param string $url Address that should be probed
* @param string $type type
*
* @return array webfinger data
* @throws HTTPException\InternalServerErrorException
*/
public static function webfinger($url, $type)
public static function webfinger(string $url, string $type): array
{
$xrd_timeout = DI::config()->get('system', 'xrd_timeout', 20);
@ -899,7 +895,7 @@ class Probe
$webfinger = json_decode($data, true);
if (!empty($webfinger)) {
if (!isset($webfinger["links"])) {
if (!isset($webfinger['links'])) {
Logger::info('No json webfinger links', ['url' => $url]);
return [];
}
@ -914,33 +910,33 @@ class Probe
}
$xrd_arr = XML::elementToArray($xrd);
if (!isset($xrd_arr["xrd"]["link"])) {
if (!isset($xrd_arr['xrd']['link'])) {
Logger::info('No XML webfinger links', ['url' => $url]);
return [];
}
$webfinger = [];
if (!empty($xrd_arr["xrd"]["subject"])) {
$webfinger["subject"] = $xrd_arr["xrd"]["subject"];
if (!empty($xrd_arr['xrd']['subject'])) {
$webfinger['subject'] = $xrd_arr['xrd']['subject'];
}
if (!empty($xrd_arr["xrd"]["alias"])) {
$webfinger["aliases"] = $xrd_arr["xrd"]["alias"];
if (!empty($xrd_arr['xrd']['alias'])) {
$webfinger['aliases'] = $xrd_arr['xrd']['alias'];
}
$webfinger["links"] = [];
$webfinger['links'] = [];
foreach ($xrd_arr["xrd"]["link"] as $value => $data) {
if (!empty($data["@attributes"])) {
$attributes = $data["@attributes"];
} elseif ($value == "@attributes") {
foreach ($xrd_arr['xrd']['link'] as $value => $data) {
if (!empty($data['@attributes'])) {
$attributes = $data['@attributes'];
} elseif ($value == '@attributes') {
$attributes = $data;
} else {
continue;
}
$webfinger["links"][] = $attributes;
$webfinger['links'][] = $attributes;
}
return $webfinger;
}
@ -953,11 +949,10 @@ class Probe
*
* @param string $noscrape_url Link to the noscrape page
* @param array $data The already fetched data
*
* @return array noscrape data
* @throws HTTPException\InternalServerErrorException
*/
private static function pollNoscrape($noscrape_url, $data)
private static function pollNoscrape(string $noscrape_url, array $data): array
{
$curlResult = DI::httpClient()->get($noscrape_url, HttpClientAccept::JSON);
if ($curlResult->isTimeout()) {
@ -976,78 +971,78 @@ class Probe
return $data;
}
if (!empty($json["fn"])) {
$data["name"] = $json["fn"];
if (!empty($json['fn'])) {
$data['name'] = $json['fn'];
}
if (!empty($json["addr"])) {
$data["addr"] = $json["addr"];
if (!empty($json['addr'])) {
$data['addr'] = $json['addr'];
}
if (!empty($json["nick"])) {
$data["nick"] = $json["nick"];
if (!empty($json['nick'])) {
$data['nick'] = $json['nick'];
}
if (!empty($json["guid"])) {
$data["guid"] = $json["guid"];
if (!empty($json['guid'])) {
$data['guid'] = $json['guid'];
}
if (!empty($json["comm"])) {
$data["community"] = $json["comm"];
if (!empty($json['comm'])) {
$data['community'] = $json['comm'];
}
if (!empty($json["tags"])) {
$keywords = implode(", ", $json["tags"]);
if ($keywords != "") {
$data["keywords"] = $keywords;
if (!empty($json['tags'])) {
$keywords = implode(', ', $json['tags']);
if ($keywords != '') {
$data['keywords'] = $keywords;
}
}
$location = Profile::formatLocation($json);
if ($location) {
$data["location"] = $location;
$data['location'] = $location;
}
if (!empty($json["about"])) {
$data["about"] = $json["about"];
if (!empty($json['about'])) {
$data['about'] = $json['about'];
}
if (!empty($json["xmpp"])) {
$data["xmpp"] = $json["xmpp"];
if (!empty($json['xmpp'])) {
$data['xmpp'] = $json['xmpp'];
}
if (!empty($json["matrix"])) {
$data["matrix"] = $json["matrix"];
if (!empty($json['matrix'])) {
$data['matrix'] = $json['matrix'];
}
if (!empty($json["key"])) {
$data["pubkey"] = $json["key"];
if (!empty($json['key'])) {
$data['pubkey'] = $json['key'];
}
if (!empty($json["photo"])) {
$data["photo"] = $json["photo"];
if (!empty($json['photo'])) {
$data['photo'] = $json['photo'];
}
if (!empty($json["dfrn-request"])) {
$data["request"] = $json["dfrn-request"];
if (!empty($json['dfrn-request'])) {
$data['request'] = $json['dfrn-request'];
}
if (!empty($json["dfrn-confirm"])) {
$data["confirm"] = $json["dfrn-confirm"];
if (!empty($json['dfrn-confirm'])) {
$data['confirm'] = $json['dfrn-confirm'];
}
if (!empty($json["dfrn-notify"])) {
$data["notify"] = $json["dfrn-notify"];
if (!empty($json['dfrn-notify'])) {
$data['notify'] = $json['dfrn-notify'];
}
if (!empty($json["dfrn-poll"])) {
$data["poll"] = $json["dfrn-poll"];
if (!empty($json['dfrn-poll'])) {
$data['poll'] = $json['dfrn-poll'];
}
if (isset($json["hide"])) {
$data["hide"] = (bool)$json["hide"];
if (isset($json['hide'])) {
$data['hide'] = (bool)$json['hide'];
} else {
$data["hide"] = false;
$data['hide'] = false;
}
return $data;
@ -1085,48 +1080,47 @@ class Probe
* Fetch data from a DFRN profile page and via "noscrape"
*
* @param string $profile_link Link to the profile page
*
* @return array profile data
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function profile($profile_link)
public static function profile(string $profile_link): array
{
$data = [];
Logger::info('Check profile', ['link' => $profile_link]);
// Fetch data via noscrape - this is faster
$noscrape_url = str_replace(["/hcard/", "/profile/"], "/noscrape/", $profile_link);
$noscrape_url = str_replace(['/hcard/', '/profile/'], '/noscrape/', $profile_link);
$data = self::pollNoscrape($noscrape_url, $data);
if (!isset($data["notify"])
|| !isset($data["confirm"])
|| !isset($data["request"])
|| !isset($data["poll"])
|| !isset($data["name"])
|| !isset($data["photo"])
if (!isset($data['notify'])
|| !isset($data['confirm'])
|| !isset($data['request'])
|| !isset($data['poll'])
|| !isset($data['name'])
|| !isset($data['photo'])
) {
$data = self::pollHcard($profile_link, $data, true);
}
$prof_data = [];
if (empty($data["addr"]) || empty($data["nick"])) {
if (empty($data['addr']) || empty($data['nick'])) {
$probe_data = self::uri($profile_link);
$data["addr"] = ($data["addr"] ?? '') ?: $probe_data["addr"];
$data["nick"] = ($data["nick"] ?? '') ?: $probe_data["nick"];
$data['addr'] = ($data['addr'] ?? '') ?: $probe_data['addr'];
$data['nick'] = ($data['nick'] ?? '') ?: $probe_data['nick'];
}
$prof_data["addr"] = $data["addr"];
$prof_data["nick"] = $data["nick"];
$prof_data["dfrn-request"] = $data['request'] ?? null;
$prof_data["dfrn-confirm"] = $data['confirm'] ?? null;
$prof_data["dfrn-notify"] = $data['notify'] ?? null;
$prof_data["dfrn-poll"] = $data['poll'] ?? null;
$prof_data["photo"] = $data['photo'] ?? null;
$prof_data["fn"] = $data['name'] ?? null;
$prof_data["key"] = $data['pubkey'] ?? null;
$prof_data['addr'] = $data['addr'];
$prof_data['nick'] = $data['nick'];
$prof_data['dfrn-request'] = $data['request'] ?? null;
$prof_data['dfrn-confirm'] = $data['confirm'] ?? null;
$prof_data['dfrn-notify'] = $data['notify'] ?? null;
$prof_data['dfrn-poll'] = $data['poll'] ?? null;
$prof_data['photo'] = $data['photo'] ?? null;
$prof_data['fn'] = $data['name'] ?? null;
$prof_data['key'] = $data['pubkey'] ?? null;
Logger::debug('Result', ['link' => $profile_link, 'data' => $prof_data]);
@ -1137,73 +1131,71 @@ class Probe
* Check for DFRN contact
*
* @param array $webfinger Webfinger data
*
* @return array DFRN data
* @throws HTTPException\InternalServerErrorException
*/
private static function dfrn($webfinger)
private static function dfrn(array $webfinger): array
{
$hcard_url = "";
$hcard_url = '';
$data = [];
// The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4
foreach (array_reverse($webfinger["links"]) as $link) {
if (($link["rel"] == ActivityNamespace::DFRN) && !empty($link["href"])) {
$data["network"] = Protocol::DFRN;
} elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) {
$data["poll"] = $link["href"];
} elseif (($link["rel"] == "http://webfinger.net/rel/profile-page") && (($link["type"] ?? "") == "text/html") && !empty($link["href"])) {
$data["url"] = $link["href"];
} elseif (($link["rel"] == "http://microformats.org/profile/hcard") && !empty($link["href"])) {
$hcard_url = $link["href"];
} elseif (($link["rel"] == ActivityNamespace::POCO) && !empty($link["href"])) {
$data["poco"] = $link["href"];
} elseif (($link["rel"] == "http://webfinger.net/rel/avatar") && !empty($link["href"])) {
$data["photo"] = $link["href"];
} elseif (($link["rel"] == "http://joindiaspora.com/seed_location") && !empty($link["href"])) {
$data["baseurl"] = trim($link["href"], '/');
} elseif (($link["rel"] == "http://joindiaspora.com/guid") && !empty($link["href"])) {
$data["guid"] = $link["href"];
} elseif (($link["rel"] == "diaspora-public-key") && !empty($link["href"])) {
$data["pubkey"] = base64_decode($link["href"]);
foreach (array_reverse($webfinger['links']) as $link) {
if (($link['rel'] == ActivityNamespace::DFRN) && !empty($link['href'])) {
$data['network'] = Protocol::DFRN;
} elseif (($link['rel'] == ActivityNamespace::FEED) && !empty($link['href'])) {
$data['poll'] = $link['href'];
} elseif (($link['rel'] == 'http://webfinger.net/rel/profile-page') && (($link['type'] ?? '') == 'text/html') && !empty($link['href'])) {
$data['url'] = $link['href'];
} elseif (($link['rel'] == 'http://microformats.org/profile/hcard') && !empty($link['href'])) {
$hcard_url = $link['href'];
} elseif (($link['rel'] == ActivityNamespace::POCO) && !empty($link['href'])) {
$data['poco'] = $link['href'];
} elseif (($link['rel'] == 'http://webfinger.net/rel/avatar') && !empty($link['href'])) {
$data['photo'] = $link['href'];
} elseif (($link['rel'] == 'http://joindiaspora.com/seed_location') && !empty($link['href'])) {
$data['baseurl'] = trim($link['href'], '/');
} elseif (($link['rel'] == 'http://joindiaspora.com/guid') && !empty($link['href'])) {
$data['guid'] = $link['href'];
} elseif (($link['rel'] == 'diaspora-public-key') && !empty($link['href'])) {
$data['pubkey'] = base64_decode($link['href']);
//if (strstr($data["pubkey"], 'RSA ') || ($link["type"] == "RSA"))
if (strstr($data["pubkey"], 'RSA ')) {
$data["pubkey"] = Crypto::rsaToPem($data["pubkey"]);
if (strstr($data['pubkey'], 'RSA ')) {
$data['pubkey'] = Crypto::rsaToPem($data['pubkey']);
}
}
}
if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
foreach ($webfinger["aliases"] as $alias) {
if (empty($data["url"]) && !strstr($alias, "@")) {
$data["url"] = $alias;
} elseif (!strstr($alias, "@") && Strings::normaliseLink($alias) != Strings::normaliseLink($data["url"])) {
$data["alias"] = $alias;
if (!empty($webfinger['aliases']) && is_array($webfinger['aliases'])) {
foreach ($webfinger['aliases'] as $alias) {
if (empty($data['url']) && !strstr($alias, '@')) {
$data['url'] = $alias;
} elseif (!strstr($alias, '@') && Strings::normaliseLink($alias) != Strings::normaliseLink($data['url'])) {
$data['alias'] = $alias;
} elseif (substr($alias, 0, 5) == 'acct:') {
$data["addr"] = substr($alias, 5);
$data['addr'] = substr($alias, 5);
}
}
}
if (!empty($webfinger["subject"]) && (substr($webfinger["subject"], 0, 5) == "acct:")) {
$data["addr"] = substr($webfinger["subject"], 5);
if (!empty($webfinger['subject']) && (substr($webfinger['subject'], 0, 5) == 'acct:')) {
$data['addr'] = substr($webfinger['subject'], 5);
}
if (!isset($data["network"]) || ($hcard_url == "")) {
if (!isset($data['network']) || ($hcard_url == '')) {
return [];
}
// Fetch data via noscrape - this is faster
$noscrape_url = str_replace("/hcard/", "/noscrape/", $hcard_url);
$noscrape_url = str_replace('/hcard/', '/noscrape/', $hcard_url);
$data = self::pollNoscrape($noscrape_url, $data);
if (isset($data["notify"])
&& isset($data["confirm"])
&& isset($data["request"])
&& isset($data["poll"])
&& isset($data["name"])
&& isset($data["photo"])
if (isset($data['notify'])
&& isset($data['confirm'])
&& isset($data['request'])
&& isset($data['poll'])
&& isset($data['name'])
&& isset($data['photo'])
) {
return $data;
}
@ -1219,11 +1211,10 @@ class Probe
* @param string $hcard_url Link to the hcard page
* @param array $data The already fetched data
* @param boolean $dfrn Poll DFRN specific data
*
* @return array hcard data
* @throws HTTPException\InternalServerErrorException
*/
private static function pollHcard($hcard_url, $data, $dfrn = false)
private static function pollHcard(string $hcard_url, array $data, bool $dfrn = false): array
{
$curlResult = DI::httpClient()->get($hcard_url, HttpClientAccept::HTML);
if ($curlResult->isTimeout()) {
@ -1278,15 +1269,15 @@ class Probe
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */
if ($search->length > 0) {
$data["pubkey"] = $search->item(0)->nodeValue;
if (strstr($data["pubkey"], 'RSA ')) {
$data["pubkey"] = Crypto::rsaToPem($data["pubkey"]);
$data['pubkey'] = $search->item(0)->nodeValue;
if (strstr($data['pubkey'], 'RSA ')) {
$data['pubkey'] = Crypto::rsaToPem($data['pubkey']);
}
}
$search = $xpath->query("//*[@id='pod_location']", $vcard); // */
if ($search->length > 0) {
$data["baseurl"] = trim($search->item(0)->nodeValue, "/");
$data['baseurl'] = trim($search->item(0)->nodeValue, '/');
}
}
@ -1299,21 +1290,21 @@ class Probe
$attr[$attribute->name] = trim($attribute->value);
}
if (isset($attr["src"]) && isset($attr["width"])) {
$avatar[$attr["width"]] = $attr["src"];
if (isset($attr['src']) && isset($attr['width'])) {
$avatar[$attr['width']] = $attr['src'];
}
// We don't have a width. So we just take everything that we got.
// This is a Hubzilla workaround which doesn't send a width.
if ((sizeof($avatar) == 0) && !empty($attr["src"])) {
$avatar[] = $attr["src"];
if ((sizeof($avatar) == 0) && !empty($attr['src'])) {
$avatar[] = $attr['src'];
}
}
}
if (sizeof($avatar)) {
ksort($avatar);
$data["photo"] = self::fixAvatar(array_pop($avatar), $data["baseurl"]);
$data['photo'] = self::fixAvatar(array_pop($avatar), $data['baseurl']);
}
if ($dfrn) {
@ -1321,19 +1312,19 @@ class Probe
$search = $xpath->query("//link[contains(concat(' ', @rel), ' dfrn-')]");
if ($search->length > 0) {
foreach ($search as $link) {
//$data["request"] = $search->item(0)->nodeValue;
//$data['request'] = $search->item(0)->nodeValue;
$attr = [];
foreach ($link->attributes as $attribute) {
$attr[$attribute->name] = trim($attribute->value);
}
$data[substr($attr["rel"], 5)] = $attr["href"];
$data[substr($attr['rel'], 5)] = $attr['href'];
}
}
// Older Friendica versions had used the "uid" field differently than newer versions
if (!empty($data["nick"]) && !empty($data["guid"]) && ($data["nick"] == $data["guid"])) {
unset($data["guid"]);
if (!empty($data['nick']) && !empty($data['guid']) && ($data['nick'] == $data['guid'])) {
unset($data['guid']);
}
}
@ -1345,64 +1336,62 @@ class Probe
* Check for Diaspora contact
*
* @param array $webfinger Webfinger data
*
* @return array Diaspora data
* @throws HTTPException\InternalServerErrorException
*/
private static function diaspora($webfinger)
private static function diaspora(array $webfinger): array
{
$hcard_url = "";
$hcard_url = '';
$data = [];
// The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4
foreach (array_reverse($webfinger["links"]) as $link) {
if (($link["rel"] == "http://microformats.org/profile/hcard") && !empty($link["href"])) {
$hcard_url = $link["href"];
} elseif (($link["rel"] == "http://joindiaspora.com/seed_location") && !empty($link["href"])) {
$data["baseurl"] = trim($link["href"], '/');
} elseif (($link["rel"] == "http://joindiaspora.com/guid") && !empty($link["href"])) {
$data["guid"] = $link["href"];
} elseif (($link["rel"] == "http://webfinger.net/rel/profile-page") && (($link["type"] ?? "") == "text/html") && !empty($link["href"])) {
$data["url"] = $link["href"];
} elseif (($link["rel"] == "http://webfinger.net/rel/profile-page") && empty($link["type"]) && !empty($link["href"])) {
$profile_url = $link["href"];
} elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) {
$data["poll"] = $link["href"];
} elseif (($link["rel"] == ActivityNamespace::POCO) && !empty($link["href"])) {
$data["poco"] = $link["href"];
} elseif (($link["rel"] == "salmon") && !empty($link["href"])) {
$data["notify"] = $link["href"];
} elseif (($link["rel"] == "diaspora-public-key") && !empty($link["href"])) {
$data["pubkey"] = base64_decode($link["href"]);
foreach (array_reverse($webfinger['links']) as $link) {
if (($link['rel'] == 'http://microformats.org/profile/hcard') && !empty($link['href'])) {
$hcard_url = $link['href'];
} elseif (($link['rel'] == 'http://joindiaspora.com/seed_location') && !empty($link['href'])) {
$data['baseurl'] = trim($link['href'], '/');
} elseif (($link['rel'] == 'http://joindiaspora.com/guid') && !empty($link['href'])) {
$data['guid'] = $link['href'];
} elseif (($link['rel'] == 'http://webfinger.net/rel/profile-page') && (($link['type'] ?? '') == 'text/html') && !empty($link['href'])) {
$data['url'] = $link['href'];
} elseif (($link['rel'] == 'http://webfinger.net/rel/profile-page') && empty($link['type']) && !empty($link['href'])) {
$profile_url = $link['href'];
} elseif (($link['rel'] == ActivityNamespace::FEED) && !empty($link['href'])) {
$data['poll'] = $link['href'];
} elseif (($link['rel'] == ActivityNamespace::POCO) && !empty($link['href'])) {
$data['poco'] = $link['href'];
} elseif (($link['rel'] == 'salmon') && !empty($link['href'])) {
$data['notify'] = $link['href'];
} elseif (($link['rel'] == 'diaspora-public-key') && !empty($link['href'])) {
$data['pubkey'] = base64_decode($link['href']);
//if (strstr($data["pubkey"], 'RSA ') || ($link["type"] == "RSA"))
if (strstr($data["pubkey"], 'RSA ')) {
$data["pubkey"] = Crypto::rsaToPem($data["pubkey"]);
if (strstr($data['pubkey'], 'RSA ')) {
$data['pubkey'] = Crypto::rsaToPem($data['pubkey']);
}
}
}
if (empty($data["url"]) && !empty($profile_url)) {
$data["url"] = $profile_url;
if (empty($data['url']) && !empty($profile_url)) {
$data['url'] = $profile_url;
}
if (empty($data["url"]) || empty($hcard_url)) {
if (empty($data['url']) || empty($hcard_url)) {
return [];
}
if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
foreach ($webfinger["aliases"] as $alias) {
if (Strings::normaliseLink($alias) != Strings::normaliseLink($data["url"]) && ! strstr($alias, "@")) {
$data["alias"] = $alias;
if (!empty($webfinger['aliases']) && is_array($webfinger['aliases'])) {
foreach ($webfinger['aliases'] as $alias) {
if (Strings::normaliseLink($alias) != Strings::normaliseLink($data['url']) && ! strstr($alias, '@')) {
$data['alias'] = $alias;
} elseif (substr($alias, 0, 5) == 'acct:') {
$data["addr"] = substr($alias, 5);
$data['addr'] = substr($alias, 5);
}
}
}
if (!empty($webfinger["subject"]) && (substr($webfinger["subject"], 0, 5) == 'acct:')) {
$data["addr"] = substr($webfinger["subject"], 5);
if (!empty($webfinger['subject']) && (substr($webfinger['subject'], 0, 5) == 'acct:')) {
$data['addr'] = substr($webfinger['subject'], 5);
}
// Fetch further information from the hcard
@ -1412,23 +1401,23 @@ class Probe
return [];
}
if (!empty($data["url"])
&& !empty($data["guid"])
&& !empty($data["baseurl"])
&& !empty($data["pubkey"])
if (!empty($data['url'])
&& !empty($data['guid'])
&& !empty($data['baseurl'])
&& !empty($data['pubkey'])
&& !empty($hcard_url)
) {
$data["network"] = Protocol::DIASPORA;
$data["manually-approve"] = false;
$data['network'] = Protocol::DIASPORA;
$data['manually-approve'] = false;
// The Diaspora handle must always be lowercase
if (!empty($data["addr"])) {
$data["addr"] = strtolower($data["addr"]);
if (!empty($data['addr'])) {
$data['addr'] = strtolower($data['addr']);
}
// We have to overwrite the detected value for "notify" since Hubzilla doesn't send it
$data["notify"] = $data["baseurl"] . "/receive/users/" . $data["guid"];
$data["batch"] = $data["baseurl"] . "/receive/public";
$data['notify'] = $data['baseurl'] . '/receive/users/' . $data['guid'];
$data['batch'] = $data['baseurl'] . '/receive/public';
} else {
return [];
}
@ -1441,7 +1430,6 @@ class Probe
*
* @param array $webfinger Webfinger data
* @param bool $short Short detection mode
*
* @return array|bool OStatus data or "false" on error or "true" on short mode
* @throws HTTPException\InternalServerErrorException
*/
@ -1449,35 +1437,35 @@ class Probe
{
$data = [];
if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
foreach ($webfinger["aliases"] as $alias) {
if (strstr($alias, "@") && !strstr(Strings::normaliseLink($alias), "http://")) {
$data["addr"] = str_replace('acct:', '', $alias);
if (!empty($webfinger['aliases']) && is_array($webfinger['aliases'])) {
foreach ($webfinger['aliases'] as $alias) {
if (strstr($alias, '@') && !strstr(Strings::normaliseLink($alias), 'http://')) {
$data['addr'] = str_replace('acct:', '', $alias);
}
}
}
if (!empty($webfinger["subject"]) && strstr($webfinger["subject"], "@")
&& !strstr(Strings::normaliseLink($webfinger["subject"]), "http://")
if (!empty($webfinger['subject']) && strstr($webfinger['subject'], '@')
&& !strstr(Strings::normaliseLink($webfinger['subject']), 'http://')
) {
$data["addr"] = str_replace('acct:', '', $webfinger["subject"]);
$data['addr'] = str_replace('acct:', '', $webfinger['subject']);
}
if (!empty($webfinger["links"])) {
if (!empty($webfinger['links'])) {
// The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4
foreach (array_reverse($webfinger["links"]) as $link) {
if (($link["rel"] == "http://webfinger.net/rel/profile-page")
&& (($link["type"] ?? "") == "text/html")
&& ($link["href"] != "")
foreach (array_reverse($webfinger['links']) as $link) {
if (($link['rel'] == 'http://webfinger.net/rel/profile-page')
&& (($link['type'] ?? '') == 'text/html')
&& ($link['href'] != '')
) {
$data["url"] = $data["alias"] = $link["href"];
} elseif (($link["rel"] == "salmon") && !empty($link["href"])) {
$data["notify"] = $link["href"];
} elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) {
$data["poll"] = $link["href"];
} elseif (($link["rel"] == "magic-public-key") && !empty($link["href"])) {
$pubkey = $link["href"];
$data['url'] = $data['alias'] = $link['href'];
} elseif (($link['rel'] == 'salmon') && !empty($link['href'])) {
$data['notify'] = $link['href'];
} elseif (($link['rel'] == ActivityNamespace::FEED) && !empty($link['href'])) {
$data['poll'] = $link['href'];
} elseif (($link['rel'] == 'magic-public-key') && !empty($link['href'])) {
$pubkey = $link['href'];
if (substr($pubkey, 0, 5) === 'data:') {
if (strstr($pubkey, ',')) {
@ -1495,23 +1483,23 @@ class Probe
$pubkey = $curlResult->getBody();
}
$key = explode(".", $pubkey);
$key = explode('.', $pubkey);
if (sizeof($key) >= 3) {
$m = Strings::base64UrlDecode($key[1]);
$e = Strings::base64UrlDecode($key[2]);
$data["pubkey"] = Crypto::meToPem($m, $e);
$data['pubkey'] = Crypto::meToPem($m, $e);
}
}
}
}
if (isset($data["notify"]) && isset($data["pubkey"])
&& isset($data["poll"])
&& isset($data["url"])
if (isset($data['notify']) && isset($data['pubkey'])
&& isset($data['poll'])
&& isset($data['url'])
) {
$data["network"] = Protocol::OSTATUS;
$data["manually-approve"] = false;
$data['network'] = Protocol::OSTATUS;
$data['manually-approve'] = false;
} else {
return $short ? false : [];
}
@ -1521,7 +1509,7 @@ class Probe
}
// Fetch all additional data from the feed
$curlResult = DI::httpClient()->get($data["poll"], HttpClientAccept::FEED_XML);
$curlResult = DI::httpClient()->get($data['poll'], HttpClientAccept::FEED_XML);
if ($curlResult->isTimeout()) {
self::$istimeout = true;
return [];
@ -1532,32 +1520,32 @@ class Probe
return [];
}
if (!empty($feed_data["header"]["author-name"])) {
$data["name"] = $feed_data["header"]["author-name"];
if (!empty($feed_data['header']['author-name'])) {
$data['name'] = $feed_data['header']['author-name'];
}
if (!empty($feed_data["header"]["author-nick"])) {
$data["nick"] = $feed_data["header"]["author-nick"];
if (!empty($feed_data['header']['author-nick'])) {
$data['nick'] = $feed_data['header']['author-nick'];
}
if (!empty($feed_data["header"]["author-avatar"])) {
$data["photo"] = self::fixAvatar($feed_data["header"]["author-avatar"], $data["url"]);
if (!empty($feed_data['header']['author-avatar'])) {
$data['photo'] = self::fixAvatar($feed_data['header']['author-avatar'], $data['url']);
}
if (!empty($feed_data["header"]["author-id"])) {
$data["alias"] = $feed_data["header"]["author-id"];
if (!empty($feed_data['header']['author-id'])) {
$data['alias'] = $feed_data['header']['author-id'];
}
if (!empty($feed_data["header"]["author-location"])) {
$data["location"] = $feed_data["header"]["author-location"];
if (!empty($feed_data['header']['author-location'])) {
$data['location'] = $feed_data['header']['author-location'];
}
if (!empty($feed_data["header"]["author-about"])) {
$data["about"] = $feed_data["header"]["author-about"];
if (!empty($feed_data['header']['author-about'])) {
$data['about'] = $feed_data['header']['author-about'];
}
// OStatus has serious issues when the the url doesn't fit (ssl vs. non ssl)
// So we take the value that we just fetched, although the other one worked as well
if (!empty($feed_data["header"]["author-link"])) {
$data["url"] = $feed_data["header"]["author-link"];
if (!empty($feed_data['header']['author-link'])) {
$data['url'] = $feed_data['header']['author-link'];
}
if ($data["url"] == $data["alias"]) {
$data["alias"] = '';
if ($data['url'] == $data['alias']) {
$data['alias'] = '';
}
/// @todo Fetch location and "about" from the feed as well
@ -1568,10 +1556,9 @@ class Probe
* Fetch data from a pump.io profile page
*
* @param string $profile_link Link to the profile page
*
* @return array profile data
* @return array Profile data
*/
private static function pumpioProfileData($profile_link)
private static function pumpioProfileData(string $profile_link): array
{
$curlResult = DI::httpClient()->get($profile_link, HttpClientAccept::HTML);
if (!$curlResult->isSuccess() || empty($curlResult->getBody())) {
@ -1586,28 +1573,27 @@ class Probe
$xpath = new DomXPath($doc);
$data = [];
$data['name'] = $xpath->query("//span[contains(@class, 'p-name')]")->item(0)->nodeValue;
$data["name"] = $xpath->query("//span[contains(@class, 'p-name')]")->item(0)->nodeValue;
if ($data["name"] == '') {
if ($data['name'] == '') {
// This is ugly - but pump.io doesn't seem to know a better way for it
$data["name"] = trim($xpath->query("//h1[@class='media-header']")->item(0)->nodeValue);
$pos = strpos($data["name"], chr(10));
$data['name'] = trim($xpath->query("//h1[@class='media-header']")->item(0)->nodeValue);
$pos = strpos($data['name'], chr(10));
if ($pos) {
$data["name"] = trim(substr($data["name"], 0, $pos));
$data['name'] = trim(substr($data['name'], 0, $pos));
}
}
$data["location"] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'p-locality')]");
$data['location'] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'p-locality')]");
if ($data["location"] == '') {
$data["location"] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'location')]");
if ($data['location'] == '') {
$data['location'] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'location')]");
}
$data["about"] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'p-note')]");
$data['about'] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'p-note')]");
if ($data["about"] == '') {
$data["about"] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'summary')]");
if ($data['about'] == '') {
$data['about'] = XML::getFirstNodeValue($xpath, "//p[contains(@class, 'summary')]");
}
$avatar = $xpath->query("//img[contains(@class, 'u-photo')]")->item(0);
@ -1616,8 +1602,8 @@ class Probe
}
if ($avatar) {
foreach ($avatar->attributes as $attribute) {
if ($attribute->name == "src") {
$data["photo"] = trim($attribute->value);
if ($attribute->name == 'src') {
$data['photo'] = trim($attribute->value);
}
}
}
@ -1632,39 +1618,39 @@ class Probe
* @param string $addr
* @return array pump.io data
*/
private static function pumpio($webfinger, $addr)
private static function pumpio(array $webfinger, string $addr): array
{
$data = [];
// The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4
foreach (array_reverse($webfinger["links"]) as $link) {
if (($link["rel"] == "http://webfinger.net/rel/profile-page")
&& (($link["type"] ?? "") == "text/html")
&& ($link["href"] != "")
foreach (array_reverse($webfinger['links']) as $link) {
if (($link['rel'] == 'http://webfinger.net/rel/profile-page')
&& (($link['type'] ?? '') == 'text/html')
&& ($link['href'] != '')
) {
$data["url"] = $link["href"];
} elseif (($link["rel"] == "activity-inbox") && ($link["href"] != "")) {
$data["notify"] = $link["href"];
} elseif (($link["rel"] == "activity-outbox") && ($link["href"] != "")) {
$data["poll"] = $link["href"];
} elseif (($link["rel"] == "dialback") && ($link["href"] != "")) {
$data["dialback"] = $link["href"];
$data['url'] = $link['href'];
} elseif (($link['rel'] == 'activity-inbox') && ($link['href'] != '')) {
$data['notify'] = $link['href'];
} elseif (($link['rel'] == 'activity-outbox') && ($link['href'] != '')) {
$data['poll'] = $link['href'];
} elseif (($link['rel'] == 'dialback') && ($link['href'] != '')) {
$data['dialback'] = $link['href'];
}
}
if (isset($data["poll"]) && isset($data["notify"])
&& isset($data["dialback"])
&& isset($data["url"])
if (isset($data['poll']) && isset($data['notify'])
&& isset($data['dialback'])
&& isset($data['url'])
) {
// by now we use these fields only for the network type detection
// So we unset all data that isn't used at the moment
unset($data["dialback"]);
unset($data['dialback']);
$data["network"] = Protocol::PUMPIO;
$data['network'] = Protocol::PUMPIO;
} else {
return [];
}
$profile_data = self::pumpioProfileData($data["url"]);
$profile_data = self::pumpioProfileData($data['url']);
if (!$profile_data) {
return [];
@ -1719,9 +1705,9 @@ class Probe
* @param string $href The potential relative href found in the HTML document
* @param string $base The HTML document URL
* @param DOMXPath $xpath The HTML document XPath
* @return string
* @return string Absolute URL
*/
private static function ensureAbsoluteLinkFromHTMLDoc(string $href, string $base, DOMXPath $xpath)
private static function ensureAbsoluteLinkFromHTMLDoc(string $href, string $base, DOMXPath $xpath): string
{
if (filter_var($href, FILTER_VALIDATE_URL)) {
return $href;
@ -1808,26 +1794,26 @@ class Probe
return self::feed($feed_url, false);
}
if (!empty($feed_data["header"]["author-name"])) {
$data["name"] = $feed_data["header"]["author-name"];
if (!empty($feed_data['header']['author-name'])) {
$data['name'] = $feed_data['header']['author-name'];
}
if (!empty($feed_data["header"]["author-nick"])) {
$data["nick"] = $feed_data["header"]["author-nick"];
if (!empty($feed_data['header']['author-nick'])) {
$data['nick'] = $feed_data['header']['author-nick'];
}
if (!empty($feed_data["header"]["author-avatar"])) {
$data["photo"] = $feed_data["header"]["author-avatar"];
if (!empty($feed_data['header']['author-avatar'])) {
$data['photo'] = $feed_data['header']['author-avatar'];
}
if (!empty($feed_data["header"]["author-id"])) {
$data["alias"] = $feed_data["header"]["author-id"];
if (!empty($feed_data['header']['author-id'])) {
$data['alias'] = $feed_data['header']['author-id'];
}
$data["url"] = $url;
$data["poll"] = $url;
$data['url'] = $url;
$data['poll'] = $url;
$data["network"] = Protocol::FEED;
$data['network'] = Protocol::FEED;
return $data;
}
@ -1837,11 +1823,10 @@ class Probe
*
* @param string $uri Profile link
* @param integer $uid User ID
*
* @return array mail data
* @throws \Exception
*/
private static function mail($uri, $uid)
private static function mail(string $uri, int $uid): array
{
if (!Network::isEmailDomainValid($uri)) {
return [];
@ -1879,14 +1864,14 @@ class Probe
$phost = substr($uri, strpos($uri, '@') + 1);
$data = [];
$data["addr"] = $uri;
$data["network"] = Protocol::MAIL;
$data["name"] = substr($uri, 0, strpos($uri, '@'));
$data["nick"] = $data["name"];
$data["photo"] = Network::lookupAvatarByEmail($uri);
$data["url"] = 'mailto:'.$uri;
$data["notify"] = 'smtp ' . Strings::getRandomHex();
$data["poll"] = 'email ' . Strings::getRandomHex();
$data['addr'] = $uri;
$data['network'] = Protocol::MAIL;
$data['name'] = substr($uri, 0, strpos($uri, '@'));
$data['nick'] = $data['name'];
$data['photo'] = Network::lookupAvatarByEmail($uri);
$data['url'] = 'mailto:'.$uri;
$data['notify'] = 'smtp ' . Strings::getRandomHex();
$data['poll'] = 'email ' . Strings::getRandomHex();
$x = Email::messageMeta($mbox, $msgs[0]);
if (stristr($x[0]->from, $uri)) {
@ -1896,17 +1881,17 @@ class Probe
}
if (isset($adr)) {
foreach ($adr as $feadr) {
if ((strcasecmp($feadr->mailbox, $data["name"]) == 0)
if ((strcasecmp($feadr->mailbox, $data['name']) == 0)
&&(strcasecmp($feadr->host, $phost) == 0)
&& (strlen($feadr->personal))
) {
$personal = imap_mime_header_decode($feadr->personal);
$data["name"] = "";
$data['name'] = '';
foreach ($personal as $perspart) {
if ($perspart->charset != "default") {
$data["name"] .= iconv($perspart->charset, 'UTF-8//IGNORE', $perspart->text);
if ($perspart->charset != 'default') {
$data['name'] .= iconv($perspart->charset, 'UTF-8//IGNORE', $perspart->text);
} else {
$data["name"] .= $perspart->text;
$data['name'] .= $perspart->text;
}
}
}
@ -1923,11 +1908,10 @@ class Probe
*
* @param string $avatar Path to the avatar
* @param string $base Another path that is hopefully complete
*
* @return string fixed avatar path
* @throws \Exception
*/
public static function fixAvatar($avatar, $base)
public static function fixAvatar(string $avatar, string $base): string
{
$base_parts = parse_url($base);
@ -1962,7 +1946,7 @@ class Probe
* @param array $data probing result
* @return string last activity
*/
public static function getLastUpdate(array $data)
public static function getLastUpdate(array $data): string
{
$uid = User::getIdForURL($data['url']);
if (!empty($uid)) {
@ -1991,9 +1975,7 @@ class Probe
* Fetch the last activity date from the "noscrape" endpoint
*
* @param array $data Probing result
* @return string last activity
*
* @return bool 'true' if update was successful or the server was unreachable
* @return string last activity or true if update was successful or the server was unreachable
*/
private static function updateFromNoScrape(array $data)
{
@ -2028,7 +2010,7 @@ class Probe
* @return string last activity
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function updateFromOutbox(string $feed, array $data)
private static function updateFromOutbox(string $feed, array $data): string
{
$outbox = ActivityPub::fetchContent($feed);
if (empty($outbox)) {
@ -2080,7 +2062,7 @@ class Probe
* @param array $data Probing result
* @return string last activity
*/
private static function updateFromFeed(array $data)
private static function updateFromFeed(array $data): string
{
// Search for the newest entry in the feed
$curlResult = DI::httpClient()->get($data['poll'], HttpClientAccept::ATOM_XML);

View file

@ -47,12 +47,13 @@ class Image
/**
* Constructor
* @param string $data
* @param boolean $type optional, default null
*
* @param string $data Image data
* @param string $type optional, default null
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public function __construct($data, $type = null)
public function __construct(string $data, string $type = null)
{
$this->imagick = class_exists('Imagick');
$this->types = Images::supportedTypes();
@ -62,12 +63,12 @@ class Image
$this->type = $type;
if ($this->isImagick() && $this->loadData($data)) {
return true;
return;
} else {
// Failed to load with Imagick, fallback
$this->imagick = false;
}
return $this->loadData($data);
$this->loadData($data);
}
/**
@ -98,12 +99,14 @@ class Image
}
/**
* @param string $data data
* @return boolean
* Loads image data into handler class
*
* @param string $data Image data
* @return boolean Success
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private function loadData($data)
private function loadData(string $data): bool
{
if ($this->isImagick()) {
$this->image = new Imagick();
@ -132,7 +135,7 @@ class Image
* setup the compression here, so we'll do it only once
*/
switch ($this->getType()) {
case "image/png":
case 'image/png':
$quality = DI::config()->get('system', 'png_quality');
/*
* From http://www.imagemagick.org/script/command-line-options.php#quality:
@ -145,7 +148,9 @@ class Image
$quality = $quality * 10;
$this->image->setCompressionQuality($quality);
break;
case "image/jpeg":
case 'image/jpg':
case 'image/jpeg':
$quality = DI::config()->get('system', 'jpeg_quality');
$this->image->setCompressionQuality($quality);
}
@ -185,7 +190,7 @@ class Image
/**
* @return boolean
*/
public function isValid()
public function isValid(): bool
{
if ($this->isImagick()) {
return ($this->image !== false);
@ -269,10 +274,12 @@ class Image
}
/**
* Scales image down
*
* @param integer $max max dimension
* @return mixed
*/
public function scaleDown($max)
public function scaleDown(int $max)
{
if (!$this->isValid()) {
return false;
@ -327,10 +334,12 @@ class Image
}
/**
* Rotates image
*
* @param integer $degrees degrees to rotate image
* @return mixed
*/
public function rotate($degrees)
public function rotate(int $degrees)
{
if (!$this->isValid()) {
return false;
@ -351,11 +360,13 @@ class Image
}
/**
* Flips image
*
* @param boolean $horiz optional, default true
* @param boolean $vert optional, default false
* @return mixed
*/
public function flip($horiz = true, $vert = false)
public function flip(bool $horiz = true, bool $vert = false)
{
if (!$this->isValid()) {
return false;
@ -391,10 +402,12 @@ class Image
}
/**
* @param string $filename filename
* Fixes orientation and maybe returns EXIF data (?)
*
* @param string $filename Filename
* @return mixed
*/
public function orient($filename)
public function orient(string $filename)
{
if ($this->isImagick()) {
// based off comment on http://php.net/manual/en/imagick.getimageorientation.php
@ -470,10 +483,12 @@ class Image
}
/**
* @param integer $min minimum dimension
* Rescales image to minimum size
*
* @param integer $min Minimum dimension
* @return mixed
*/
public function scaleUp($min)
public function scaleUp(int $min)
{
if (!$this->isValid()) {
return false;
@ -513,10 +528,12 @@ class Image
}
/**
* @param integer $dim dimension
* Scales image to square
*
* @param integer $dim Dimension
* @return mixed
*/
public function scaleToSquare($dim)
public function scaleToSquare(int $dim)
{
if (!$this->isValid()) {
return false;
@ -528,11 +545,11 @@ class Image
/**
* Scale image to target dimensions
*
* @param int $dest_width
* @param int $dest_height
* @return boolean
* @param int $dest_width Destination width
* @param int $dest_height Destination height
* @return boolean Success
*/
private function scale($dest_width, $dest_height)
private function scale(int $dest_width, int $dest_height): bool
{
if (!$this->isValid()) {
return false;
@ -584,6 +601,8 @@ class Image
/**
* Convert a GIF to a PNG to make it static
*
* @return void
*/
public function toStatic()
{
@ -598,6 +617,8 @@ class Image
}
/**
* Crops image
*
* @param integer $max maximum
* @param integer $x x coordinate
* @param integer $y y coordinate
@ -605,7 +626,7 @@ class Image
* @param integer $h height
* @return mixed
*/
public function crop($max, $x, $y, $w, $h)
public function crop(int $max, int $x, int $y, int $w, int $h)
{
if (!$this->isValid()) {
return false;
@ -638,6 +659,9 @@ class Image
$this->image = $dest;
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
// All successful
return true;
}
/**
@ -650,11 +674,14 @@ class Image
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public function __toString() {
return $this->asString();
public function __toString(): string
{
return (string) $this->asString();
}
/**
* Returns image as string or false on failure
*
* @return mixed
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
@ -681,13 +708,16 @@ class Image
imageinterlace($this->image, true);
switch ($this->getType()) {
case "image/png":
case 'image/png':
$quality = DI::config()->get('system', 'png_quality');
imagepng($this->image, null, $quality);
break;
case "image/jpeg":
case 'image/jpeg':
case 'image/jpg':
$quality = DI::config()->get('system', 'jpeg_quality');
imagejpeg($this->image, null, $quality);
break;
}
$string = ob_get_contents();
ob_end_clean();

View file

@ -22,6 +22,8 @@
namespace Friendica\Protocol;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMXPath;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Logger;
@ -120,7 +122,7 @@ class DFRN
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = self::addHeader($doc, $owner, "dfrn:owner", "", false);
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
if (! count($items)) {
return trim($doc->saveXML());
@ -129,10 +131,10 @@ class DFRN
foreach ($items as $item) {
// These values aren't sent when sending from the queue.
/// @todo Check if we can set these values from the queue or if they are needed at all.
$item["entry:comment-allow"] = ($item["entry:comment-allow"] ?? '') ?: true;
$item["entry:cid"] = $item["entry:cid"] ?? 0;
$item['entry:comment-allow'] = ($item['entry:comment-allow'] ?? '') ?: true;
$item['entry:cid'] = $item['entry:cid'] ?? 0;
$entry = self::entry($doc, "text", $item, $owner, $item["entry:comment-allow"], $item["entry:cid"]);
$entry = self::entry($doc, 'text', $item, $owner, $item['entry:comment-allow'], $item['entry:cid']);
if (isset($entry)) {
$root->appendChild($entry);
}
@ -186,17 +188,15 @@ class DFRN
$root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed');
$doc->appendChild($root);
$root->setAttribute("xmlns:thr", ActivityNamespace::THREAD);
$root->setAttribute("xmlns:at", ActivityNamespace::TOMB);
$root->setAttribute("xmlns:media", ActivityNamespace::MEDIA);
$root->setAttribute("xmlns:dfrn", ActivityNamespace::DFRN);
$root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY);
$root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS);
$root->setAttribute("xmlns:poco", ActivityNamespace::POCO);
$root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS);
$root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET);
//$root = self::addHeader($doc, $owner, "dfrn:owner", "", false);
$root->setAttribute('xmlns:thr', ActivityNamespace::THREAD);
$root->setAttribute('xmlns:at', ActivityNamespace::TOMB);
$root->setAttribute('xmlns:media', ActivityNamespace::MEDIA);
$root->setAttribute('xmlns:dfrn', ActivityNamespace::DFRN);
$root->setAttribute('xmlns:activity', ActivityNamespace::ACTIVITY);
$root->setAttribute('xmlns:georss', ActivityNamespace::GEORSS);
$root->setAttribute('xmlns:poco', ActivityNamespace::POCO);
$root->setAttribute('xmlns:ostatus', ActivityNamespace::OSTATUS);
$root->setAttribute('xmlns:statusnet', ActivityNamespace::STATUSNET);
foreach ($items as $item) {
$entry = self::entry($doc, $type, $item, $owner, true, 0);
@ -227,22 +227,22 @@ class DFRN
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = self::addHeader($doc, $owner, "dfrn:owner", "", false);
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
$mailElement = $doc->createElement("dfrn:mail");
$senderElement = $doc->createElement("dfrn:sender");
$mailElement = $doc->createElement('dfrn:mail');
$senderElement = $doc->createElement('dfrn:sender');
XML::addElement($doc, $senderElement, "dfrn:name", $owner['name']);
XML::addElement($doc, $senderElement, "dfrn:uri", $owner['url']);
XML::addElement($doc, $senderElement, "dfrn:avatar", $owner['thumb']);
XML::addElement($doc, $senderElement, 'dfrn:name', $owner['name']);
XML::addElement($doc, $senderElement, 'dfrn:uri', $owner['url']);
XML::addElement($doc, $senderElement, 'dfrn:avatar', $owner['thumb']);
$mailElement->appendChild($senderElement);
XML::addElement($doc, $mailElement, "dfrn:id", $mail['uri']);
XML::addElement($doc, $mailElement, "dfrn:in-reply-to", $mail['parent-uri']);
XML::addElement($doc, $mailElement, "dfrn:sentdate", DateTimeFormat::utc($mail['created'] . '+00:00', DateTimeFormat::ATOM));
XML::addElement($doc, $mailElement, "dfrn:subject", $mail['title']);
XML::addElement($doc, $mailElement, "dfrn:content", $mail['body']);
XML::addElement($doc, $mailElement, 'dfrn:id', $mail['uri']);
XML::addElement($doc, $mailElement, 'dfrn:in-reply-to', $mail['parent-uri']);
XML::addElement($doc, $mailElement, 'dfrn:sentdate', DateTimeFormat::utc($mail['created'] . '+00:00', DateTimeFormat::ATOM));
XML::addElement($doc, $mailElement, 'dfrn:subject', $mail['title']);
XML::addElement($doc, $mailElement, 'dfrn:content', $mail['body']);
$root->appendChild($mailElement);
@ -264,15 +264,15 @@ class DFRN
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = self::addHeader($doc, $owner, "dfrn:owner", "", false);
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
$suggest = $doc->createElement("dfrn:suggest");
$suggest = $doc->createElement('dfrn:suggest');
XML::addElement($doc, $suggest, "dfrn:url", $item['url']);
XML::addElement($doc, $suggest, "dfrn:name", $item['name']);
XML::addElement($doc, $suggest, "dfrn:photo", $item['photo']);
XML::addElement($doc, $suggest, "dfrn:request", $item['request']);
XML::addElement($doc, $suggest, "dfrn:note", $item['note']);
XML::addElement($doc, $suggest, 'dfrn:url', $item['url']);
XML::addElement($doc, $suggest, 'dfrn:name', $item['name']);
XML::addElement($doc, $suggest, 'dfrn:photo', $item['photo']);
XML::addElement($doc, $suggest, 'dfrn:request', $item['request']);
XML::addElement($doc, $suggest, 'dfrn:note', $item['note']);
$root->appendChild($suggest);
@ -313,22 +313,22 @@ class DFRN
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = self::addHeader($doc, $owner, "dfrn:owner", "", false);
$root = self::addHeader($doc, $owner, 'dfrn:owner', '', false);
$relocate = $doc->createElement("dfrn:relocate");
$relocate = $doc->createElement('dfrn:relocate');
XML::addElement($doc, $relocate, "dfrn:url", $owner['url']);
XML::addElement($doc, $relocate, "dfrn:name", $owner['name']);
XML::addElement($doc, $relocate, "dfrn:addr", $owner['addr']);
XML::addElement($doc, $relocate, "dfrn:avatar", $owner['avatar']);
XML::addElement($doc, $relocate, "dfrn:photo", $photos[4]);
XML::addElement($doc, $relocate, "dfrn:thumb", $photos[5]);
XML::addElement($doc, $relocate, "dfrn:micro", $photos[6]);
XML::addElement($doc, $relocate, "dfrn:request", $owner['request']);
XML::addElement($doc, $relocate, "dfrn:confirm", $owner['confirm']);
XML::addElement($doc, $relocate, "dfrn:notify", $owner['notify']);
XML::addElement($doc, $relocate, "dfrn:poll", $owner['poll']);
XML::addElement($doc, $relocate, "dfrn:sitepubkey", DI::config()->get('system', 'site_pubkey'));
XML::addElement($doc, $relocate, 'dfrn:url', $owner['url']);
XML::addElement($doc, $relocate, 'dfrn:name', $owner['name']);
XML::addElement($doc, $relocate, 'dfrn:addr', $owner['addr']);
XML::addElement($doc, $relocate, 'dfrn:avatar', $owner['avatar']);
XML::addElement($doc, $relocate, 'dfrn:photo', $photos[4]);
XML::addElement($doc, $relocate, 'dfrn:thumb', $photos[5]);
XML::addElement($doc, $relocate, 'dfrn:micro', $photos[6]);
XML::addElement($doc, $relocate, 'dfrn:request', $owner['request']);
XML::addElement($doc, $relocate, 'dfrn:confirm', $owner['confirm']);
XML::addElement($doc, $relocate, 'dfrn:notify', $owner['notify']);
XML::addElement($doc, $relocate, 'dfrn:poll', $owner['poll']);
XML::addElement($doc, $relocate, 'dfrn:sitepubkey', DI::config()->get('system', 'site_pubkey'));
$root->appendChild($relocate);
@ -343,69 +343,67 @@ class DFRN
* @param string $authorelement Element name for the author
* @param string $alternatelink link to profile or category
* @param bool $public Is it a header for public posts?
*
* @return object XML root object
* @return DOMElement XML root element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Find proper type-hint for returned type
*/
private static function addHeader(DOMDocument $doc, array $owner, string $authorelement, string $alternatelink = '', bool $public = false)
private static function addHeader(DOMDocument $doc, array $owner, string $authorelement, string $alternatelink = '', bool $public = false): DOMElement
{
if ($alternatelink == "") {
if ($alternatelink == '') {
$alternatelink = $owner['url'];
}
$root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed');
$doc->appendChild($root);
$root->setAttribute("xmlns:thr", ActivityNamespace::THREAD);
$root->setAttribute("xmlns:at", ActivityNamespace::TOMB);
$root->setAttribute("xmlns:media", ActivityNamespace::MEDIA);
$root->setAttribute("xmlns:dfrn", ActivityNamespace::DFRN);
$root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY);
$root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS);
$root->setAttribute("xmlns:poco", ActivityNamespace::POCO);
$root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS);
$root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET);
$root->setAttribute('xmlns:thr', ActivityNamespace::THREAD);
$root->setAttribute('xmlns:at', ActivityNamespace::TOMB);
$root->setAttribute('xmlns:media', ActivityNamespace::MEDIA);
$root->setAttribute('xmlns:dfrn', ActivityNamespace::DFRN);
$root->setAttribute('xmlns:activity', ActivityNamespace::ACTIVITY);
$root->setAttribute('xmlns:georss', ActivityNamespace::GEORSS);
$root->setAttribute('xmlns:poco', ActivityNamespace::POCO);
$root->setAttribute('xmlns:ostatus', ActivityNamespace::OSTATUS);
$root->setAttribute('xmlns:statusnet', ActivityNamespace::STATUSNET);
XML::addElement($doc, $root, "id", DI::baseUrl()."/profile/".$owner["nick"]);
XML::addElement($doc, $root, "title", $owner["name"]);
XML::addElement($doc, $root, 'id', DI::baseUrl() . '/profile/' . $owner['nick']);
XML::addElement($doc, $root, 'title', $owner['name']);
$attributes = ["uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION];
XML::addElement($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes);
$attributes = ['uri' => 'https://friendi.ca', 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION];
XML::addElement($doc, $root, 'generator', FRIENDICA_PLATFORM, $attributes);
$attributes = ["rel" => "license", "href" => "http://creativecommons.org/licenses/by/3.0/"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['rel' => 'license', 'href' => 'http://creativecommons.org/licenses/by/3.0/'];
XML::addElement($doc, $root, 'link', '', $attributes);
$attributes = ["rel" => "alternate", "type" => "text/html", "href" => $alternatelink];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['rel' => 'alternate', 'type' => 'text/html', 'href' => $alternatelink];
XML::addElement($doc, $root, 'link', '', $attributes);
if ($public) {
// DFRN itself doesn't uses this. But maybe someone else wants to subscribe to the public feed.
OStatus::hublinks($doc, $root, $owner["nick"]);
OStatus::addHubLink($doc, $root, $owner['nick']);
$attributes = ["rel" => "salmon", "href" => DI::baseUrl()."/salmon/".$owner["nick"]];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['rel' => 'salmon', 'href' => DI::baseUrl() . '/salmon/' . $owner['nick']];
XML::addElement($doc, $root, 'link', '', $attributes);
$attributes = ["rel" => "http://salmon-protocol.org/ns/salmon-replies", "href" => DI::baseUrl()."/salmon/".$owner["nick"]];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['rel' => 'http://salmon-protocol.org/ns/salmon-replies', 'href' => DI::baseUrl() . '/salmon/' . $owner['nick']];
XML::addElement($doc, $root, 'link', '', $attributes);
$attributes = ["rel" => "http://salmon-protocol.org/ns/salmon-mention", "href" => DI::baseUrl()."/salmon/".$owner["nick"]];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['rel' => 'http://salmon-protocol.org/ns/salmon-mention', 'href' => DI::baseUrl() . '/salmon/' . $owner['nick']];
XML::addElement($doc, $root, 'link', '', $attributes);
}
// For backward compatibility we keep this element
if ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY) {
XML::addElement($doc, $root, "dfrn:community", 1);
XML::addElement($doc, $root, 'dfrn:community', 1);
}
// The former element is replaced by this one
XML::addElement($doc, $root, "dfrn:account_type", $owner["account-type"]);
XML::addElement($doc, $root, 'dfrn:account_type', $owner['account-type']);
/// @todo We need a way to transmit the different page flags like "User::PAGE_FLAGS_PRVGROUP"
XML::addElement($doc, $root, "updated", DateTimeFormat::utcNow(DateTimeFormat::ATOM));
XML::addElement($doc, $root, 'updated', DateTimeFormat::utcNow(DateTimeFormat::ATOM));
$author = self::addAuthor($doc, $owner, $authorelement, $public);
$root->appendChild($author);
@ -466,12 +464,11 @@ class DFRN
* @param array $owner Owner record
* @param string $authorelement Element name for the author
* @param boolean $public boolean
*
* @return \DOMElement XML author object
* @return DOMElement XML author object
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Find proper type-hints
*/
private static function addAuthor(DOMDocument $doc, array $owner, string $authorelement, bool $public)
private static function addAuthor(DOMDocument $doc, array $owner, string $authorelement, bool $public): DOMElement
{
// Should the profile be "unsearchable" in the net? Then add the "hide" element
$hide = DBA::exists('profile', ['uid' => $owner['uid'], 'net-publish' => false]);
@ -484,28 +481,32 @@ class DFRN
$attributes = [];
if (!$public || !$hide) {
$attributes = ["dfrn:updated" => $namdate];
$attributes = ['dfrn:updated' => $namdate];
}
XML::addElement($doc, $author, "name", $owner["name"], $attributes);
XML::addElement($doc, $author, "uri", DI::baseUrl().'/profile/'.$owner["nickname"], $attributes);
XML::addElement($doc, $author, "dfrn:handle", $owner["addr"], $attributes);
XML::addElement($doc, $author, 'name', $owner['name'], $attributes);
XML::addElement($doc, $author, 'uri', DI::baseUrl().'/profile/' . $owner['nickname'], $attributes);
XML::addElement($doc, $author, 'dfrn:handle', $owner['addr'], $attributes);
$attributes = ["rel" => "photo", "type" => "image/jpeg",
"media:width" => Proxy::PIXEL_SMALL, "media:height" => Proxy::PIXEL_SMALL,
"href" => User::getAvatarUrl($owner, Proxy::SIZE_SMALL)];
$attributes = [
'rel' => 'photo',
'type' => 'image/jpeg',
'media:width' => Proxy::PIXEL_SMALL,
'media:height' => Proxy::PIXEL_SMALL,
'href' => User::getAvatarUrl($owner, Proxy::SIZE_SMALL),
];
if (!$public || !$hide) {
$attributes["dfrn:updated"] = $picdate;
$attributes['dfrn:updated'] = $picdate;
}
XML::addElement($doc, $author, "link", "", $attributes);
XML::addElement($doc, $author, 'link', '', $attributes);
$attributes["rel"] = "avatar";
XML::addElement($doc, $author, "link", "", $attributes);
$attributes['rel'] = 'avatar';
XML::addElement($doc, $author, 'link', '', $attributes);
if ($hide) {
XML::addElement($doc, $author, "dfrn:hide", "true");
XML::addElement($doc, $author, 'dfrn:hide', 'true');
}
// The following fields will only be generated if the data isn't meant for a public feed
@ -516,7 +517,7 @@ class DFRN
$birthday = self::determineNextBirthday($owner['uid'], $owner['timezone']);
if ($birthday) {
XML::addElement($doc, $author, "dfrn:birthday", $birthday);
XML::addElement($doc, $author, 'dfrn:birthday', $birthday);
}
// Only show contact details when we are allowed to
@ -524,57 +525,57 @@ class DFRN
['about', 'name', 'homepage', 'nickname', 'timezone', 'locality', 'region', 'country-name', 'pub_keywords', 'xmpp', 'dob'],
['uid' => $owner['uid'], 'hidewall' => false]);
if (DBA::isResult($profile)) {
XML::addElement($doc, $author, "poco:displayName", $profile["name"]);
XML::addElement($doc, $author, "poco:updated", $namdate);
XML::addElement($doc, $author, 'poco:displayName', $profile['name']);
XML::addElement($doc, $author, 'poco:updated', $namdate);
if (trim($profile["dob"]) > DBA::NULL_DATE) {
XML::addElement($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"])));
if (trim($profile['dob']) > DBA::NULL_DATE) {
XML::addElement($doc, $author, 'poco:birthday', '0000-'.date('m-d', strtotime($profile['dob'])));
}
XML::addElement($doc, $author, "poco:note", $profile["about"]);
XML::addElement($doc, $author, "poco:preferredUsername", $profile["nickname"]);
XML::addElement($doc, $author, 'poco:note', $profile['about']);
XML::addElement($doc, $author, 'poco:preferredUsername', $profile['nickname']);
XML::addElement($doc, $author, "poco:utcOffset", DateTimeFormat::timezoneNow($profile["timezone"], "P"));
XML::addElement($doc, $author, 'poco:utcOffset', DateTimeFormat::timezoneNow($profile['timezone'], 'P'));
if (trim($profile["homepage"]) != "") {
$urls = $doc->createElement("poco:urls");
XML::addElement($doc, $urls, "poco:type", "homepage");
XML::addElement($doc, $urls, "poco:value", $profile["homepage"]);
XML::addElement($doc, $urls, "poco:primary", "true");
if (trim($profile['homepage']) != '') {
$urls = $doc->createElement('poco:urls');
XML::addElement($doc, $urls, 'poco:type', 'homepage');
XML::addElement($doc, $urls, 'poco:value', $profile['homepage']);
XML::addElement($doc, $urls, 'poco:primary', 'true');
$author->appendChild($urls);
}
if (trim($profile["pub_keywords"]) != "") {
$keywords = explode(",", $profile["pub_keywords"]);
if (trim($profile['pub_keywords']) != '') {
$keywords = explode(',', $profile['pub_keywords']);
foreach ($keywords as $keyword) {
XML::addElement($doc, $author, "poco:tags", trim($keyword));
XML::addElement($doc, $author, 'poco:tags', trim($keyword));
}
}
if (trim($profile["xmpp"]) != "") {
$ims = $doc->createElement("poco:ims");
XML::addElement($doc, $ims, "poco:type", "xmpp");
XML::addElement($doc, $ims, "poco:value", $profile["xmpp"]);
XML::addElement($doc, $ims, "poco:primary", "true");
if (trim($profile['xmpp']) != '') {
$ims = $doc->createElement('poco:ims');
XML::addElement($doc, $ims, 'poco:type', 'xmpp');
XML::addElement($doc, $ims, 'poco:value', $profile['xmpp']);
XML::addElement($doc, $ims, 'poco:primary', 'true');
$author->appendChild($ims);
}
if (trim($profile["locality"].$profile["region"].$profile["country-name"]) != "") {
$element = $doc->createElement("poco:address");
if (trim($profile['locality'] . $profile['region'] . $profile['country-name']) != '') {
$element = $doc->createElement('poco:address');
XML::addElement($doc, $element, "poco:formatted", Profile::formatLocation($profile));
XML::addElement($doc, $element, 'poco:formatted', Profile::formatLocation($profile));
if (trim($profile["locality"]) != "") {
XML::addElement($doc, $element, "poco:locality", $profile["locality"]);
if (trim($profile['locality']) != '') {
XML::addElement($doc, $element, 'poco:locality', $profile['locality']);
}
if (trim($profile["region"]) != "") {
XML::addElement($doc, $element, "poco:region", $profile["region"]);
if (trim($profile['region']) != '') {
XML::addElement($doc, $element, 'poco:region', $profile['region']);
}
if (trim($profile["country-name"]) != "") {
XML::addElement($doc, $element, "poco:country", $profile["country-name"]);
if (trim($profile['country-name']) != '') {
XML::addElement($doc, $element, 'poco:country', $profile['country-name']);
}
$author->appendChild($element);
@ -588,42 +589,43 @@ class DFRN
* Adds the author elements in the "entry" elements of the DFRN protocol
*
* @param DOMDocument $doc XML document
* @param string $element Element name for the author
* @param string $contact_url Link of the contact
* @param array $item Item elements
*
* @return \DOMElement XML author object
* @param string $element Element name for the author
* @param string $contact_url Link of the contact
* @param array $item Item elements
* @return DOMElement XML author object
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Find proper type-hints
*/
private static function addEntryAuthor(DOMDocument $doc, string $element, string $contact_url, array $item)
private static function addEntryAuthor(DOMDocument $doc, string $element, string $contact_url, array $item): DOMElement
{
$author = $doc->createElement($element);
$contact = Contact::getByURLForUser($contact_url, $item["uid"], false, ['url', 'name', 'addr', 'photo']);
$contact = Contact::getByURLForUser($contact_url, $item['uid'], false, ['url', 'name', 'addr', 'photo']);
if (!empty($contact)) {
XML::addElement($doc, $author, "name", $contact["name"]);
XML::addElement($doc, $author, "uri", $contact["url"]);
XML::addElement($doc, $author, "dfrn:handle", $contact["addr"]);
XML::addElement($doc, $author, 'name', $contact['name']);
XML::addElement($doc, $author, 'uri', $contact['url']);
XML::addElement($doc, $author, 'dfrn:handle', $contact['addr']);
/// @Todo
/// - Check real image type and image size
/// - Check which of these boths elements we should use
$attributes = [
"rel" => "photo",
"type" => "image/jpeg",
"media:width" => 80,
"media:height" => 80,
"href" => $contact["photo"]];
XML::addElement($doc, $author, "link", "", $attributes);
'rel' => 'photo',
'type' => 'image/jpeg',
'media:width' => 80,
'media:height' => 80,
'href' => $contact['photo'],
];
XML::addElement($doc, $author, 'link', '', $attributes);
$attributes = [
"rel" => "avatar",
"type" => "image/jpeg",
"media:width" => 80,
"media:height" => 80,
"href" => $contact["photo"]];
XML::addElement($doc, $author, "link", "", $attributes);
'rel' => 'avatar',
'type' => 'image/jpeg',
'media:width' => 80,
'media:height' => 80,
'href' => $contact['photo'],
];
XML::addElement($doc, $author, 'link', '', $attributes);
}
return $author;
@ -636,8 +638,7 @@ class DFRN
* @param string $element Element name for the activity
* @param string $activity activity value
* @param int $uriid Uri-Id of the post
*
* @return \DOMElement XML activity object
* @return DOMElement XML activity object
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Find proper type-hints
*/
@ -825,91 +826,95 @@ class DFRN
}
$attributes = [
"href" => $conversation_href,
"ref" => $conversation_uri];
'href' => $conversation_href,
'ref' => $conversation_uri,
];
XML::addElement($doc, $entry, "ostatus:conversation", $conversation_uri, $attributes);
XML::addElement($doc, $entry, 'ostatus:conversation', $conversation_uri, $attributes);
XML::addElement($doc, $entry, "id", $item["uri"]);
XML::addElement($doc, $entry, "title", $item["title"]);
XML::addElement($doc, $entry, 'id', $item['uri']);
XML::addElement($doc, $entry, 'title', $item['title']);
XML::addElement($doc, $entry, "published", DateTimeFormat::utc($item["created"] . "+00:00", DateTimeFormat::ATOM));
XML::addElement($doc, $entry, "updated", DateTimeFormat::utc($item["edited"] . "+00:00", DateTimeFormat::ATOM));
XML::addElement($doc, $entry, 'published', DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM));
XML::addElement($doc, $entry, 'updated', DateTimeFormat::utc($item['edited'] . '+00:00', DateTimeFormat::ATOM));
// "dfrn:env" is used to read the content
XML::addElement($doc, $entry, "dfrn:env", Strings::base64UrlEncode($body, true));
XML::addElement($doc, $entry, 'dfrn:env', Strings::base64UrlEncode($body, true));
// The "content" field is not read by the receiver. We could remove it when the type is "text"
// We keep it at the moment, maybe there is some old version that doesn't read "dfrn:env"
XML::addElement($doc, $entry, "content", (($type == 'html') ? $htmlbody : $body), ["type" => $type]);
XML::addElement($doc, $entry, 'content', (($type == 'html') ? $htmlbody : $body), ['type' => $type]);
// We save this value in "plink". Maybe we should read it from there as well?
XML::addElement(
$doc,
$entry,
"link",
"",
["rel" => "alternate", "type" => "text/html",
"href" => DI::baseUrl() . "/display/" . $item["guid"]]
'link',
'',
[
'rel' => 'alternate',
'type' => 'text/html',
'href' => DI::baseUrl() . '/display/' . $item['guid']
],
);
// "comment-allow" is some old fashioned stuff for old Friendica versions.
// It is included in the rewritten code for completeness
if ($comment) {
XML::addElement($doc, $entry, "dfrn:comment-allow", 1);
XML::addElement($doc, $entry, 'dfrn:comment-allow', 1);
}
if ($item['location']) {
XML::addElement($doc, $entry, "dfrn:location", $item['location']);
XML::addElement($doc, $entry, 'dfrn:location', $item['location']);
}
if ($item['coord']) {
XML::addElement($doc, $entry, "georss:point", $item['coord']);
XML::addElement($doc, $entry, 'georss:point', $item['coord']);
}
if ($item['private']) {
// Friendica versions prior to 2020.3 can't handle "unlisted" properly. So we can only transmit public and private
XML::addElement($doc, $entry, "dfrn:private", ($item['private'] == Item::PRIVATE ? Item::PRIVATE : Item::PUBLIC));
XML::addElement($doc, $entry, "dfrn:unlisted", $item['private'] == Item::UNLISTED);
XML::addElement($doc, $entry, 'dfrn:private', ($item['private'] == Item::PRIVATE ? Item::PRIVATE : Item::PUBLIC));
XML::addElement($doc, $entry, 'dfrn:unlisted', $item['private'] == Item::UNLISTED);
}
if ($item['extid']) {
XML::addElement($doc, $entry, "dfrn:extid", $item['extid']);
XML::addElement($doc, $entry, 'dfrn:extid', $item['extid']);
}
if ($item['post-type'] == Item::PT_PAGE) {
XML::addElement($doc, $entry, "dfrn:bookmark", "true");
XML::addElement($doc, $entry, 'dfrn:bookmark', 'true');
}
if ($item['app']) {
XML::addElement($doc, $entry, "statusnet:notice_info", "", ["local_id" => $item['id'], "source" => $item['app']]);
XML::addElement($doc, $entry, 'statusnet:notice_info', '', ['local_id' => $item['id'], 'source' => $item['app']]);
}
XML::addElement($doc, $entry, "dfrn:diaspora_guid", $item["guid"]);
XML::addElement($doc, $entry, 'dfrn:diaspora_guid', $item['guid']);
// The signed text contains the content in Markdown, the sender handle and the signatur for the content
// It is needed for relayed comments to Diaspora.
if ($item['signed_text']) {
$sign = base64_encode(json_encode(['signed_text' => $item['signed_text'],'signature' => '','signer' => '']));
XML::addElement($doc, $entry, "dfrn:diaspora_signature", $sign);
XML::addElement($doc, $entry, 'dfrn:diaspora_signature', $sign);
}
XML::addElement($doc, $entry, "activity:verb", self::constructVerb($item));
XML::addElement($doc, $entry, 'activity:verb', self::constructVerb($item));
if ($item['object-type'] != "") {
XML::addElement($doc, $entry, "activity:object-type", $item['object-type']);
if ($item['object-type'] != '') {
XML::addElement($doc, $entry, 'activity:object-type', $item['object-type']);
} elseif ($item['gravity'] == GRAVITY_PARENT) {
XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::NOTE);
XML::addElement($doc, $entry, 'activity:object-type', Activity\ObjectType::NOTE);
} else {
XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::COMMENT);
XML::addElement($doc, $entry, 'activity:object-type', Activity\ObjectType::COMMENT);
}
$actobj = self::createActivity($doc, "activity:object", $item['object'] ?? '', $item['uri-id']);
$actobj = self::createActivity($doc, 'activity:object', $item['object'] ?? '', $item['uri-id']);
if ($actobj) {
$entry->appendChild($actobj);
}
$actarg = self::createActivity($doc, "activity:target", $item['target'] ?? '', $item['uri-id']);
$actarg = self::createActivity($doc, 'activity:target', $item['target'] ?? '', $item['uri-id']);
if ($actarg) {
$entry->appendChild($actarg);
}
@ -919,7 +924,7 @@ class DFRN
if (count($tags)) {
foreach ($tags as $tag) {
if (($type != 'html') || ($tag['type'] == Tag::HASHTAG)) {
XML::addElement($doc, $entry, "category", "", ["scheme" => "X-DFRN:" . Tag::TAG_CHARACTER[$tag['type']] . ":" . $tag['url'], "term" => $tag['name']]);
XML::addElement($doc, $entry, 'category', '', ['scheme' => 'X-DFRN:' . Tag::TAG_CHARACTER[$tag['type']] . ':' . $tag['url'], 'term' => $tag['name']]);
}
if ($tag['type'] != Tag::HASHTAG) {
$mentioned[$tag['url']] = $tag['url'];
@ -928,28 +933,32 @@ class DFRN
}
foreach ($mentioned as $mention) {
$condition = ['uid' => $owner["uid"], 'nurl' => Strings::normaliseLink($mention)];
$condition = ['uid' => $owner['uid'], 'nurl' => Strings::normaliseLink($mention)];
$contact = DBA::selectFirst('contact', ['contact-type'], $condition);
if (DBA::isResult($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) {
XML::addElement(
$doc,
$entry,
"link",
"",
["rel" => "mentioned",
"ostatus:object-type" => Activity\ObjectType::GROUP,
"href" => $mention]
'link',
'',
[
'rel' => 'mentioned',
'ostatus:object-type' => Activity\ObjectType::GROUP,
'href' => $mention,
],
);
} else {
XML::addElement(
$doc,
$entry,
"link",
"",
["rel" => "mentioned",
"ostatus:object-type" => Activity\ObjectType::PERSON,
"href" => $mention]
'link',
'',
[
'rel' => 'mentioned',
'ostatus:object-type' => Activity\ObjectType::PERSON,
'href' => $mention,
],
);
}
}
@ -999,22 +1008,22 @@ class DFRN
$envelope = Diaspora::buildMessage($atom, $owner, $contact, $owner['uprvkey'], $pubkey, $public_batch);
// Create the endpoint for public posts. This is some WIP and should later be added to the probing
if ($public_batch && empty($contact["batch"])) {
$parts = parse_url($contact["notify"]);
if ($public_batch && empty($contact['batch'])) {
$parts = parse_url($contact['notify']);
$path_parts = explode('/', $parts['path']);
array_pop($path_parts);
$parts['path'] = implode('/', $path_parts);
$contact["batch"] = Network::unparseURL($parts);
$contact['batch'] = Network::unparseURL($parts);
}
$dest_url = ($public_batch ? $contact["batch"] : $contact["notify"]);
$dest_url = ($public_batch ? $contact['batch'] : $contact['notify']);
if (empty($dest_url)) {
Logger::info('Empty destination', ['public' => $public_batch, 'contact' => $contact]);
return -24;
}
$content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json");
$content_type = ($public_batch ? 'application/magic-envelope+xml' : 'application/json');
$postResult = DI::httpClient()->post($dest_url, $envelope, ['Content-Type' => $content_type]);
$xml = $postResult->getBody();
@ -1331,28 +1340,27 @@ class DFRN
/**
* Processes the mail elements
*
* @param object $xpath XPath object
* @param object $mail mail elements
* @param array $importer Record of the importer user mixed with contact of the content
* @param DOMXPath $xpath XPath object
* @param DOMNode $mail mail elements
* @param array $importer Record of the importer user mixed with contact of the content
* @return void
* @throws \Exception
* @todo Find good type-hints for all parameter
*/
private static function processMail($xpath, $mail, array $importer)
private static function processMail(DOMXPath $xpath, DOMNode $mail, array $importer)
{
Logger::notice("Processing mails");
$msg = [];
$msg["uid"] = $importer["importer_uid"];
$msg["from-name"] = XML::getFirstValue($xpath, "dfrn:sender/dfrn:name/text()", $mail);
$msg["from-url"] = XML::getFirstValue($xpath, "dfrn:sender/dfrn:uri/text()", $mail);
$msg["from-photo"] = XML::getFirstValue($xpath, "dfrn:sender/dfrn:avatar/text()", $mail);
$msg["contact-id"] = $importer["id"];
$msg["uri"] = XML::getFirstValue($xpath, "dfrn:id/text()", $mail);
$msg["parent-uri"] = XML::getFirstValue($xpath, "dfrn:in-reply-to/text()", $mail);
$msg["created"] = DateTimeFormat::utc(XML::getFirstValue($xpath, "dfrn:sentdate/text()", $mail));
$msg["title"] = XML::getFirstValue($xpath, "dfrn:subject/text()", $mail);
$msg["body"] = XML::getFirstValue($xpath, "dfrn:content/text()", $mail);
$msg['uid'] = $importer['importer_uid'];
$msg['from-name'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:name/text()', $mail);
$msg['from-url'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:uri/text()', $mail);
$msg['from-photo'] = XML::getFirstValue($xpath, 'dfrn:sender/dfrn:avatar/text()', $mail);
$msg['contact-id'] = $importer['id'];
$msg['uri'] = XML::getFirstValue($xpath, 'dfrn:id/text()', $mail);
$msg['parent-uri'] = XML::getFirstValue($xpath, 'dfrn:in-reply-to/text()', $mail);
$msg['created'] = DateTimeFormat::utc(XML::getFirstValue($xpath, 'dfrn:sentdate/text()', $mail));
$msg['title'] = XML::getFirstValue($xpath, 'dfrn:subject/text()', $mail);
$msg['body'] = XML::getFirstValue($xpath, 'dfrn:content/text()', $mail);
Mail::insert($msg);
}
@ -1360,14 +1368,13 @@ class DFRN
/**
* Processes the suggestion elements
*
* @param object $xpath XPath object
* @param object $suggestion suggestion elements
* @param array $importer Record of the importer user mixed with contact of the content
* @param DOMXPath $xpath XPath object
* @param DOMNode $suggestion suggestion elements
* @param array $importer Record of the importer user mixed with contact of the content
* @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Find good type-hints for all parameter
*/
private static function processSuggestion($xpath, $suggestion, array $importer)
private static function processSuggestion(DOMXPath $xpath, DOMNode $suggestion, array $importer)
{
Logger::notice('Processing suggestions');
@ -1435,45 +1442,45 @@ class DFRN
/**
* Processes the relocation elements
*
* @param object $xpath XPath object
* @param object $relocation relocation elements
* @param array $importer Record of the importer user mixed with contact of the content
* @param DOMXPath $xpath XPath object
* @param DOMNode $relocation relocation elements
* @param array $importer Record of the importer user mixed with contact of the content
* @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
* @todo Find good type-hints for all parameter
*/
private static function processRelocation($xpath, $relocation, array $importer): bool
private static function processRelocation(DOMXPath $xpath, DOMNode $relocation, array $importer): bool
{
Logger::notice("Processing relocations");
/// @TODO Rewrite this to one statement
$relocate = [];
$relocate["uid"] = $importer["importer_uid"];
$relocate["cid"] = $importer["id"];
$relocate["url"] = $xpath->query("dfrn:url/text()", $relocation)->item(0)->nodeValue;
$relocate["addr"] = $xpath->query("dfrn:addr/text()", $relocation)->item(0)->nodeValue;
$relocate["name"] = $xpath->query("dfrn:name/text()", $relocation)->item(0)->nodeValue;
$relocate["avatar"] = $xpath->query("dfrn:avatar/text()", $relocation)->item(0)->nodeValue;
$relocate["photo"] = $xpath->query("dfrn:photo/text()", $relocation)->item(0)->nodeValue;
$relocate["thumb"] = $xpath->query("dfrn:thumb/text()", $relocation)->item(0)->nodeValue;
$relocate["micro"] = $xpath->query("dfrn:micro/text()", $relocation)->item(0)->nodeValue;
$relocate["request"] = $xpath->query("dfrn:request/text()", $relocation)->item(0)->nodeValue;
$relocate["confirm"] = $xpath->query("dfrn:confirm/text()", $relocation)->item(0)->nodeValue;
$relocate["notify"] = $xpath->query("dfrn:notify/text()", $relocation)->item(0)->nodeValue;
$relocate["poll"] = $xpath->query("dfrn:poll/text()", $relocation)->item(0)->nodeValue;
$relocate["sitepubkey"] = $xpath->query("dfrn:sitepubkey/text()", $relocation)->item(0)->nodeValue;
$relocate['uid'] = $importer['importer_uid'];
$relocate['cid'] = $importer['id'];
$relocate['url'] = $xpath->query('dfrn:url/text()', $relocation)->item(0)->nodeValue;
$relocate['addr'] = $xpath->query('dfrn:addr/text()', $relocation)->item(0)->nodeValue;
$relocate['name'] = $xpath->query('dfrn:name/text()', $relocation)->item(0)->nodeValue;
$relocate['avatar'] = $xpath->query('dfrn:avatar/text()', $relocation)->item(0)->nodeValue;
$relocate['photo'] = $xpath->query('dfrn:photo/text()', $relocation)->item(0)->nodeValue;
$relocate['thumb'] = $xpath->query('dfrn:thumb/text()', $relocation)->item(0)->nodeValue;
$relocate['micro'] = $xpath->query('dfrn:micro/text()', $relocation)->item(0)->nodeValue;
$relocate['request'] = $xpath->query('dfrn:request/text()', $relocation)->item(0)->nodeValue;
$relocate['confirm'] = $xpath->query('dfrn:confirm/text()', $relocation)->item(0)->nodeValue;
$relocate['notify'] = $xpath->query('dfrn:notify/text()', $relocation)->item(0)->nodeValue;
$relocate['poll'] = $xpath->query('dfrn:poll/text()', $relocation)->item(0)->nodeValue;
$relocate['sitepubkey'] = $xpath->query('dfrn:sitepubkey/text()', $relocation)->item(0)->nodeValue;
if (($relocate["avatar"] == "") && ($relocate["photo"] != "")) {
$relocate["avatar"] = $relocate["photo"];
if (($relocate['avatar'] == '') && ($relocate['photo'] != '')) {
$relocate['avatar'] = $relocate['photo'];
}
if ($relocate["addr"] == "") {
$relocate["addr"] = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$3@$2", $relocate["url"]);
if ($relocate['addr'] == '') {
$relocate['addr'] = preg_replace("=(https?://)(.*)/profile/(.*)=ism", '$3@$2', $relocate['url']);
}
// update contact
$old = Contact::selectFirst(['photo', 'url'], ['id' => $importer["id"], 'uid' => $importer["importer_uid"]]);
$old = Contact::selectFirst(['photo', 'url'], ['id' => $importer['id'], 'uid' => $importer['importer_uid']]);
if (!DBA::isResult($old)) {
Logger::notice("Query failed to execute, no result returned in " . __FUNCTION__);
@ -1481,16 +1488,23 @@ class DFRN
}
// Update the contact table. We try to find every entry.
$fields = ['name' => $relocate["name"], 'avatar' => $relocate["avatar"],
'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]),
'addr' => $relocate["addr"], 'request' => $relocate["request"],
'confirm' => $relocate["confirm"], 'notify' => $relocate["notify"],
'poll' => $relocate["poll"], 'site-pubkey' => $relocate["sitepubkey"]];
$condition = ["(`id` = ?) OR (`nurl` = ?)", $importer["id"], Strings::normaliseLink($old["url"])];
$fields = [
'name' => $relocate['name'],
'avatar' => $relocate['avatar'],
'url' => $relocate['url'],
'nurl' => Strings::normaliseLink($relocate['url']),
'addr' => $relocate['addr'],
'request' => $relocate['request'],
'confirm' => $relocate['confirm'],
'notify' => $relocate['notify'],
'poll' => $relocate['poll'],
'site-pubkey' => $relocate['sitepubkey'],
];
$condition = ["(`id` = ?) OR (`nurl` = ?)", $importer['id'], Strings::normaliseLink($old['url'])];
Contact::update($fields, $condition);
Contact::updateAvatar($importer["id"], $relocate["avatar"], true);
Contact::updateAvatar($importer['id'], $relocate['avatar'], true);
Logger::notice('Contacts are updated.');
@ -1519,15 +1533,18 @@ class DFRN
if (self::isEditedTimestampNewer($current, $item)) {
// do not accept (ignore) an earlier edit than one we currently have.
if (DateTimeFormat::utc($item["edited"]) < $current["edited"]) {
if (DateTimeFormat::utc($item['edited']) < $current['edited']) {
return false;
}
$fields = ['title' => $item['title'] ?? '', 'body' => $item['body'] ?? '',
'changed' => DateTimeFormat::utcNow(),
'edited' => DateTimeFormat::utc($item["edited"])];
$fields = [
'title' => $item['title'] ?? '',
'body' => $item['body'] ?? '',
'changed' => DateTimeFormat::utcNow(),
'edited' => DateTimeFormat::utc($item['edited']),
];
$condition = ["`uri` = ? AND `uid` IN (0, ?)", $item["uri"], $importer["importer_uid"]];
$condition = ["`uri` = ? AND `uid` IN (0, ?)", $item['uri'], $importer['importer_uid']];
Item::update($fields, $condition);
$changed = true;
@ -1547,11 +1564,11 @@ class DFRN
*/
private static function getEntryType(array $importer, array $item): int
{
if ($item["thr-parent"] != $item["uri"]) {
if ($item['thr-parent'] != $item['uri']) {
$community = false;
if ($importer['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) {
$sql_extra = "";
$sql_extra = '';
$community = true;
Logger::notice("possible community action");
} else {
@ -1561,7 +1578,7 @@ class DFRN
// was the top-level post for this action written by somebody on this site?
// Specifically, the recipient?
$parent = Post::selectFirst(['wall'],
["`uri` = ? AND `uid` = ?" . $sql_extra, $item["thr-parent"], $importer["importer_uid"]]);
["`uri` = ? AND `uid` = ?" . $sql_extra, $item['thr-parent'], $importer['importer_uid']]);
$is_a_remote_action = DBA::isResult($parent);
@ -1586,43 +1603,44 @@ class DFRN
*/
private static function doPoke(array $item, array $importer)
{
$verb = urldecode(substr($item["verb"], strpos($item["verb"], "#")+1));
$verb = urldecode(substr($item['verb'], strpos($item['verb'], '#')+1));
if (!$verb) {
return;
}
$xo = XML::parseString($item["object"]);
$xo = XML::parseString($item['object']);
if (($xo->type == Activity\ObjectType::PERSON) && ($xo->id)) {
// somebody was poked/prodded. Was it me?
$Blink = '';
foreach ($xo->link as $l) {
$atts = $l->attributes();
switch ($atts["rel"]) {
case "alternate":
$Blink = $atts["href"];
switch ($atts['rel']) {
case 'alternate':
$Blink = $atts['href'];
break;
default:
break;
}
}
if ($Blink && Strings::compareLink($Blink, DI::baseUrl() . "/profile/" . $importer["nickname"])) {
if ($Blink && Strings::compareLink($Blink, DI::baseUrl() . '/profile/' . $importer['nickname'])) {
$author = DBA::selectFirst('contact', ['id', 'name', 'thumb', 'url'], ['id' => $item['author-id']]);
$parent = Post::selectFirst(['id'], ['uri' => $item['thr-parent'], 'uid' => $importer["importer_uid"]]);
$parent = Post::selectFirst(['id'], ['uri' => $item['thr-parent'], 'uid' => $importer['importer_uid']]);
$item['parent'] = $parent['id'];
// send a notification
DI::notify()->createFromArray(
[
"type" => Notification\Type::POKE,
"otype" => Notification\ObjectType::PERSON,
"activity" => $verb,
"verb" => $item["verb"],
"uid" => $importer["importer_uid"],
"cid" => $author["id"],
"item" => $item,
"link" => DI::baseUrl() . "/display/" . urlencode($item['guid']),
'type' => Notification\Type::POKE,
'otype' => Notification\ObjectType::PERSON,
'activity' => $verb,
'verb' => $item['verb'],
'uid' => $importer['importer_uid'],
'cid' => $author['id'],
'item' => $item,
'link' => DI::baseUrl() . '/display/' . urlencode($item['guid']),
]
);
}
@ -1639,11 +1657,10 @@ class DFRN
*
* @return bool Should the processing of the entries be continued?
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo set proper type-hints (array?)
*/
private static function processVerbs(int $entrytype, array $importer, array &$item, bool &$is_like)
{
Logger::info("Process verb ".$item["verb"]." and object-type ".$item["object-type"]." for entrytype ".$entrytype);
Logger::info('Process verb ' . $item['verb'] . ' and object-type ' . $item['object-type'] . ' for entrytype ' . $entrytype);
if (($entrytype == DFRN::TOP_LEVEL) && !empty($importer['id'])) {
// The filling of the the "contact" variable is done for legcy reasons
@ -1654,65 +1671,70 @@ class DFRN
// Big question: Do we need these functions? They were part of the "consume_feed" function.
// This function once was responsible for DFRN and OStatus.
if ($activity->match($item["verb"], Activity::FOLLOW)) {
if ($activity->match($item['verb'], Activity::FOLLOW)) {
Logger::notice("New follower");
Contact::addRelationship($importer, $contact, $item);
return false;
}
if ($activity->match($item["verb"], Activity::UNFOLLOW)) {
if ($activity->match($item['verb'], Activity::UNFOLLOW)) {
Logger::notice("Lost follower");
Contact::removeFollower($contact);
return false;
}
if ($activity->match($item["verb"], Activity::REQ_FRIEND)) {
if ($activity->match($item['verb'], Activity::REQ_FRIEND)) {
Logger::notice("New friend request");
Contact::addRelationship($importer, $contact, $item, true);
return false;
}
if ($activity->match($item["verb"], Activity::UNFRIEND)) {
if ($activity->match($item['verb'], Activity::UNFRIEND)) {
Logger::notice("Lost sharer");
Contact::removeSharer($contact);
return false;
}
} else {
if (($item["verb"] == Activity::LIKE)
|| ($item["verb"] == Activity::DISLIKE)
|| ($item["verb"] == Activity::ATTEND)
|| ($item["verb"] == Activity::ATTENDNO)
|| ($item["verb"] == Activity::ATTENDMAYBE)
|| ($item["verb"] == Activity::ANNOUNCE)
if (($item['verb'] == Activity::LIKE)
|| ($item['verb'] == Activity::DISLIKE)
|| ($item['verb'] == Activity::ATTEND)
|| ($item['verb'] == Activity::ATTENDNO)
|| ($item['verb'] == Activity::ATTENDMAYBE)
|| ($item['verb'] == Activity::ANNOUNCE)
) {
$is_like = true;
$item["gravity"] = GRAVITY_ACTIVITY;
$item['gravity'] = GRAVITY_ACTIVITY;
// only one like or dislike per person
// split into two queries for performance issues
$condition = ['uid' => $item["uid"], 'author-id' => $item["author-id"], 'gravity' => GRAVITY_ACTIVITY,
'verb' => $item['verb'], 'parent-uri' => $item['thr-parent']];
$condition = [
'uid' => $item['uid'],
'author-id' => $item['author-id'],
'gravity' => GRAVITY_ACTIVITY,
'verb' => $item['verb'],
'parent-uri' => $item['thr-parent'],
];
if (Post::exists($condition)) {
return false;
}
$condition = ['uid' => $item["uid"], 'author-id' => $item["author-id"], 'gravity' => GRAVITY_ACTIVITY,
$condition = ['uid' => $item['uid'], 'author-id' => $item['author-id'], 'gravity' => GRAVITY_ACTIVITY,
'verb' => $item['verb'], 'thr-parent' => $item['thr-parent']];
if (Post::exists($condition)) {
return false;
}
// The owner of an activity must be the author
$item["owner-name"] = $item["author-name"];
$item["owner-link"] = $item["author-link"];
$item["owner-avatar"] = $item["author-avatar"];
$item["owner-id"] = $item["author-id"];
$item['owner-name'] = $item['author-name'];
$item['owner-link'] = $item['author-link'];
$item['owner-avatar'] = $item['author-avatar'];
$item['owner-id'] = $item['author-id'];
} else {
$is_like = false;
}
if (($item["verb"] == Activity::TAG) && ($item["object-type"] == Activity\ObjectType::TAGTERM)) {
$xo = XML::parseString($item["object"]);
$xt = XML::parseString($item["target"]);
if (($item['verb'] == Activity::TAG) && ($item['object-type'] == Activity\ObjectType::TAGTERM)) {
$xo = XML::parseString($item['object']);
$xt = XML::parseString($item['target']);
if ($xt->type == Activity\ObjectType::NOTE) {
$item_tag = Post::selectFirst(['id', 'uri-id'], ['uri' => $xt->id, 'uid' => $importer["importer_uid"]]);
$item_tag = Post::selectFirst(['id', 'uri-id'], ['uri' => $xt->id, 'uid' => $importer['importer_uid']]);
if (!DBA::isResult($item_tag)) {
Logger::notice("Query failed to execute, no result returned in " . __FUNCTION__);
@ -1739,27 +1761,28 @@ class DFRN
*/
private static function parseLinks($links, array &$item)
{
$rel = "";
$href = "";
$rel = '';
$href = '';
$type = null;
$length = null;
$title = null;
foreach ($links as $link) {
foreach ($link->attributes as $attributes) {
switch ($attributes->name) {
case "href" : $href = $attributes->textContent; break;
case "rel" : $rel = $attributes->textContent; break;
case "type" : $type = $attributes->textContent; break;
case "length": $length = $attributes->textContent; break;
case "title" : $title = $attributes->textContent; break;
case 'href' : $href = $attributes->textContent; break;
case 'rel' : $rel = $attributes->textContent; break;
case 'type' : $type = $attributes->textContent; break;
case 'length': $length = $attributes->textContent; break;
case 'title' : $title = $attributes->textContent; break;
}
}
if (($rel != "") && ($href != "")) {
if (($rel != '') && ($href != '')) {
switch ($rel) {
case "alternate":
$item["plink"] = $href;
case 'alternate':
$item['plink'] = $href;
break;
case "enclosure":
case 'enclosure':
Post\Media::insert(['uri-id' => $item['uri-id'], 'type' => Post\Media::DOCUMENT,
'url' => $href, 'mimetype' => $type, 'size' => $length, 'description' => $title]);
break;
@ -1805,162 +1828,159 @@ class DFRN
/**
* Processes the entry elements which contain the items and comments
*
* @param array $header Array of the header elements that always stay the same
* @param object $xpath XPath object
* @param object $entry entry elements
* @param array $importer Record of the importer user mixed with contact of the content
* @param string $xml xml
* @param array $header Array of the header elements that always stay the same
* @param DOMXPath $xpath XPath object
* @param DOMNode $entry entry elements
* @param array $importer Record of the importer user mixed with contact of the content
* @param string $xml XML
* @param int $protocol Protocol
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
* @todo Add type-hints
*/
private static function processEntry(array $header, $xpath, $entry, array $importer, string $xml, int $protocol)
private static function processEntry(array $header, DOMXPath $xpath, DOMNode $entry, array $importer, string $xml, int $protocol)
{
Logger::notice("Processing entries");
$item = $header;
$item["protocol"] = $protocol;
$item['protocol'] = $protocol;
$item["source"] = $xml;
$item['source'] = $xml;
// Get the uri
$item["uri"] = XML::getFirstNodeValue($xpath, "atom:id/text()", $entry);
$item['uri'] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry);
$item["edited"] = XML::getFirstNodeValue($xpath, "atom:updated/text()", $entry);
$item['edited'] = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry);
$current = Post::selectFirst(['id', 'uid', 'edited', 'body'],
['uri' => $item["uri"], 'uid' => $importer["importer_uid"]]
['uri' => $item['uri'], 'uid' => $importer['importer_uid']]
);
// Is there an existing item?
if (DBA::isResult($current) && !self::isEditedTimestampNewer($current, $item)) {
Logger::info("Item ".$item["uri"]." (".$item['edited'].") already existed.");
Logger::info("Item " . $item['uri'] . " (" . $item['edited'] . ") already existed.");
return;
}
// Fetch the owner
$owner = self::fetchauthor($xpath, $entry, $importer, "dfrn:owner", true, $xml);
$owner = self::fetchauthor($xpath, $entry, $importer, 'dfrn:owner', true, $xml);
$owner_unknown = (isset($owner["contact-unknown"]) && $owner["contact-unknown"]);
$owner_unknown = (isset($owner['contact-unknown']) && $owner['contact-unknown']);
$item["owner-name"] = $owner["name"];
$item["owner-link"] = $owner["link"];
$item["owner-avatar"] = $owner["avatar"];
$item["owner-id"] = Contact::getIdForURL($owner["link"], 0);
$item['owner-name'] = $owner['name'];
$item['owner-link'] = $owner['link'];
$item['owner-avatar'] = $owner['avatar'];
$item['owner-id'] = Contact::getIdForURL($owner['link'], 0);
// fetch the author
$author = self::fetchauthor($xpath, $entry, $importer, "atom:author", true, $xml);
$author = self::fetchauthor($xpath, $entry, $importer, 'atom:author', true, $xml);
$item["author-name"] = $author["name"];
$item["author-link"] = $author["link"];
$item["author-avatar"] = $author["avatar"];
$item["author-id"] = Contact::getIdForURL($author["link"], 0);
$item['author-name'] = $author['name'];
$item['author-link'] = $author['link'];
$item['author-avatar'] = $author['avatar'];
$item['author-id'] = Contact::getIdForURL($author['link'], 0);
$item["title"] = XML::getFirstNodeValue($xpath, "atom:title/text()", $entry);
$item['title'] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry);
if (!empty($item["title"])) {
$item["post-type"] = Item::PT_ARTICLE;
if (!empty($item['title'])) {
$item['post-type'] = Item::PT_ARTICLE;
} else {
$item["post-type"] = Item::PT_NOTE;
$item['post-type'] = Item::PT_NOTE;
}
$item["created"] = XML::getFirstNodeValue($xpath, "atom:published/text()", $entry);
$item['created'] = XML::getFirstNodeValue($xpath, 'atom:published/text()', $entry);
$item["body"] = XML::getFirstNodeValue($xpath, "dfrn:env/text()", $entry);
$item["body"] = str_replace([' ',"\t","\r","\n"], ['','','',''], $item["body"]);
$item['body'] = XML::getFirstNodeValue($xpath, 'dfrn:env/text()', $entry);
$item['body'] = str_replace([' ',"\t","\r","\n"], ['','','',''], $item['body']);
$item["body"] = Strings::base64UrlDecode($item["body"]);
$item['body'] = Strings::base64UrlDecode($item['body']);
$item["body"] = BBCode::limitBodySize($item["body"]);
$item['body'] = BBCode::limitBodySize($item['body']);
/// @todo We should check for a repeated post and if we know the repeated author.
// We don't need the content element since "dfrn:env" is always present
//$item["body"] = $xpath->query("atom:content/text()", $entry)->item(0)->nodeValue;
//$item['body'] = $xpath->query('atom:content/text()', $entry)->item(0)->nodeValue;
$item['location'] = XML::getFirstNodeValue($xpath, 'dfrn:location/text()', $entry);
$item['coord'] = XML::getFirstNodeValue($xpath, 'georss:point', $entry);
$item['private'] = XML::getFirstNodeValue($xpath, 'dfrn:private/text()', $entry);
$item["location"] = XML::getFirstNodeValue($xpath, "dfrn:location/text()", $entry);
$item["coord"] = XML::getFirstNodeValue($xpath, "georss:point", $entry);
$item["private"] = XML::getFirstNodeValue($xpath, "dfrn:private/text()", $entry);
$unlisted = XML::getFirstNodeValue($xpath, "dfrn:unlisted/text()", $entry);
$unlisted = XML::getFirstNodeValue($xpath, 'dfrn:unlisted/text()', $entry);
if (!empty($unlisted) && ($item['private'] != Item::PRIVATE)) {
$item['private'] = Item::UNLISTED;
}
$item["extid"] = XML::getFirstNodeValue($xpath, "dfrn:extid/text()", $entry);
$item['extid'] = XML::getFirstNodeValue($xpath, 'dfrn:extid/text()', $entry);
if (XML::getFirstNodeValue($xpath, "dfrn:bookmark/text()", $entry) == "true") {
$item["post-type"] = Item::PT_PAGE;
if (XML::getFirstNodeValue($xpath, 'dfrn:bookmark/text()', $entry) == 'true') {
$item['post-type'] = Item::PT_PAGE;
}
$notice_info = $xpath->query("statusnet:notice_info", $entry);
$notice_info = $xpath->query('statusnet:notice_info', $entry);
if ($notice_info && ($notice_info->length > 0)) {
foreach ($notice_info->item(0)->attributes as $attributes) {
if ($attributes->name == "source") {
$item["app"] = strip_tags($attributes->textContent);
if ($attributes->name == 'source') {
$item['app'] = strip_tags($attributes->textContent);
}
}
}
$item["guid"] = XML::getFirstNodeValue($xpath, "dfrn:diaspora_guid/text()", $entry);
$item['guid'] = XML::getFirstNodeValue($xpath, 'dfrn:diaspora_guid/text()', $entry);
$item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
$item["body"] = Item::improveSharedDataInBody($item);
$item['body'] = Item::improveSharedDataInBody($item);
Tag::storeFromBody($item['uri-id'], $item["body"]);
Tag::storeFromBody($item['uri-id'], $item['body']);
// We store the data from "dfrn:diaspora_signature" in a different table, this is done in "Item::insert"
$dsprsig = XML::unescape(XML::getFirstNodeValue($xpath, "dfrn:diaspora_signature/text()", $entry));
if ($dsprsig != "") {
$dsprsig = XML::unescape(XML::getFirstNodeValue($xpath, 'dfrn:diaspora_signature/text()', $entry));
if ($dsprsig != '') {
$signature = json_decode(base64_decode($dsprsig));
// We don't store the old style signatures anymore that also contained the "signature" and "signer"
if (!empty($signature->signed_text) && empty($signature->signature) && empty($signature->signer)) {
$item["diaspora_signed_text"] = $signature->signed_text;
$item['diaspora_signed_text'] = $signature->signed_text;
}
}
$item["verb"] = XML::getFirstNodeValue($xpath, "activity:verb/text()", $entry);
$item['verb'] = XML::getFirstNodeValue($xpath, 'activity:verb/text()', $entry);
if (XML::getFirstNodeValue($xpath, "activity:object-type/text()", $entry) != "") {
$item["object-type"] = XML::getFirstNodeValue($xpath, "activity:object-type/text()", $entry);
if (XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $entry) != '') {
$item['object-type'] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $entry);
}
$object = $xpath->query("activity:object", $entry)->item(0);
$item["object"] = self::transformActivity($xpath, $object, "object");
$object = $xpath->query('activity:object', $entry)->item(0);
$item['object'] = self::transformActivity($xpath, $object, 'object');
if (trim($item["object"]) != "") {
$r = XML::parseString($item["object"]);
if (trim($item['object']) != '') {
$r = XML::parseString($item['object']);
if (isset($r->type)) {
$item["object-type"] = $r->type;
$item['object-type'] = $r->type;
}
}
$target = $xpath->query("activity:target", $entry)->item(0);
$item["target"] = self::transformActivity($xpath, $target, "target");
$target = $xpath->query('activity:target', $entry)->item(0);
$item['target'] = self::transformActivity($xpath, $target, 'target');
$categories = $xpath->query("atom:category", $entry);
$categories = $xpath->query('atom:category', $entry);
if ($categories) {
foreach ($categories as $category) {
$term = "";
$scheme = "";
$term = '';
$scheme = '';
foreach ($category->attributes as $attributes) {
if ($attributes->name == "term") {
if ($attributes->name == 'term') {
$term = $attributes->textContent;
}
if ($attributes->name == "scheme") {
if ($attributes->name == 'scheme') {
$scheme = $attributes->textContent;
}
}
if (($term != "") && ($scheme != "")) {
$parts = explode(":", $scheme);
if ((count($parts) >= 4) && (array_shift($parts) == "X-DFRN")) {
if (($term != '') && ($scheme != '')) {
$parts = explode(':', $scheme);
if ((count($parts) >= 4) && (array_shift($parts) == 'X-DFRN')) {
$termurl = array_pop($parts);
$termurl = array_pop($parts) . ':' . $termurl;
Tag::store($item['uri-id'], Tag::IMPLICIT_MENTION, $term, $termurl);
@ -1969,7 +1989,7 @@ class DFRN
}
}
$links = $xpath->query("atom:link", $entry);
$links = $xpath->query('atom:link', $entry);
if ($links) {
self::parseLinks($links, $item);
}
@ -1979,10 +1999,10 @@ class DFRN
$conv = $xpath->query('ostatus:conversation', $entry);
if (is_object($conv->item(0))) {
foreach ($conv->item(0)->attributes as $attributes) {
if ($attributes->name == "ref") {
if ($attributes->name == 'ref') {
$item['conversation-uri'] = $attributes->textContent;
}
if ($attributes->name == "href") {
if ($attributes->name == 'href') {
$item['conversation-href'] = $attributes->textContent;
}
}
@ -1991,10 +2011,10 @@ class DFRN
// Is it a reply or a top level posting?
$item['thr-parent'] = $item['uri'];
$inreplyto = $xpath->query("thr:in-reply-to", $entry);
$inreplyto = $xpath->query('thr:in-reply-to', $entry);
if (is_object($inreplyto->item(0))) {
foreach ($inreplyto->item(0)->attributes as $attributes) {
if ($attributes->name == "ref") {
if ($attributes->name == 'ref') {
$item['thr-parent'] = $attributes->textContent;
}
}
@ -2011,24 +2031,24 @@ class DFRN
// Now assign the rest of the values that depend on the type of the message
if (in_array($entrytype, [DFRN::REPLY, DFRN::REPLY_RC])) {
if (!isset($item["object-type"])) {
$item["object-type"] = Activity\ObjectType::COMMENT;
if (!isset($item['object-type'])) {
$item['object-type'] = Activity\ObjectType::COMMENT;
}
if ($item["contact-id"] != $owner["contact-id"]) {
$item["contact-id"] = $owner["contact-id"];
if ($item['contact-id'] != $owner['contact-id']) {
$item['contact-id'] = $owner['contact-id'];
}
if (($item["network"] != $owner["network"]) && ($owner["network"] != "")) {
$item["network"] = $owner["network"];
if (($item['network'] != $owner['network']) && ($owner['network'] != '')) {
$item['network'] = $owner['network'];
}
if ($item["contact-id"] != $author["contact-id"]) {
$item["contact-id"] = $author["contact-id"];
if ($item['contact-id'] != $author['contact-id']) {
$item['contact-id'] = $author['contact-id'];
}
if (($item["network"] != $author["network"]) && ($author["network"] != "")) {
$item["network"] = $author["network"];
if (($item['network'] != $author['network']) && ($author['network'] != '')) {
$item['network'] = $author['network'];
}
}
@ -2036,34 +2056,34 @@ class DFRN
$item = Item::addShareDataFromOriginal($item);
if ($entrytype == DFRN::REPLY_RC) {
$item["wall"] = 1;
$item['wall'] = 1;
} elseif ($entrytype == DFRN::TOP_LEVEL) {
if (!isset($item["object-type"])) {
$item["object-type"] = Activity\ObjectType::NOTE;
if (!isset($item['object-type'])) {
$item['object-type'] = Activity\ObjectType::NOTE;
}
// Is it an event?
if (($item["object-type"] == Activity\ObjectType::EVENT) && !$owner_unknown) {
Logger::info("Item ".$item["uri"]." seems to contain an event.");
$ev = Event::fromBBCode($item["body"]);
if (($item['object-type'] == Activity\ObjectType::EVENT) && !$owner_unknown) {
Logger::info("Item " . $item['uri'] . " seems to contain an event.");
$ev = Event::fromBBCode($item['body']);
if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
Logger::info("Event in item ".$item["uri"]." was found.");
$ev["cid"] = $importer["id"];
$ev["uid"] = $importer["importer_uid"];
$ev["uri"] = $item["uri"];
$ev["edited"] = $item["edited"];
$ev["private"] = $item["private"];
$ev["guid"] = $item["guid"];
$ev["plink"] = $item["plink"];
$ev["network"] = $item["network"];
$ev["protocol"] = $item["protocol"];
$ev["direction"] = $item["direction"];
$ev["source"] = $item["source"];
Logger::info("Event in item " . $item['uri'] . " was found.");
$ev['cid'] = $importer['id'];
$ev['uid'] = $importer['importer_uid'];
$ev['uri'] = $item['uri'];
$ev['edited'] = $item['edited'];
$ev['private'] = $item['private'];
$ev['guid'] = $item['guid'];
$ev['plink'] = $item['plink'];
$ev['network'] = $item['network'];
$ev['protocol'] = $item['protocol'];
$ev['direction'] = $item['direction'];
$ev['source'] = $item['source'];
$condition = ['uri' => $item["uri"], 'uid' => $importer["importer_uid"]];
$condition = ['uri' => $item['uri'], 'uid' => $importer['importer_uid']];
$event = DBA::selectFirst('event', ['id'], $condition);
if (DBA::isResult($event)) {
$ev["id"] = $event["id"];
$ev['id'] = $event['id'];
}
$event_id = Event::store($ev);
@ -2084,7 +2104,7 @@ class DFRN
// This check is done here to be able to receive connection requests in "processVerbs"
if (($entrytype == DFRN::TOP_LEVEL) && $owner_unknown) {
Logger::info("Item won't be stored because user " . $importer["importer_uid"] . " doesn't follow " . $item["owner-link"] . ".");
Logger::info("Item won't be stored because user " . $importer['importer_uid'] . " doesn't follow " . $item['owner-link'] . ".");
return;
}
@ -2092,9 +2112,9 @@ class DFRN
// Update content if 'updated' changes
if (DBA::isResult($current)) {
if (self::updateContent($current, $item, $importer, $entrytype)) {
Logger::info("Item ".$item["uri"]." was updated.");
Logger::info("Item " . $item['uri'] . " was updated.");
} else {
Logger::info("Item " . $item["uri"] . " already existed.");
Logger::info("Item " . $item['uri'] . " already existed.");
}
return;
}
@ -2107,7 +2127,7 @@ class DFRN
$posted_id = Item::insert($item);
if ($posted_id) {
Logger::info("Reply from contact ".$item["contact-id"]." was stored with id ".$posted_id);
Logger::info("Reply from contact " . $item['contact-id'] . " was stored with id " . $posted_id);
if ($item['uid'] == 0) {
Item::distribute($posted_id);
@ -2116,11 +2136,11 @@ class DFRN
return true;
}
} else { // $entrytype == DFRN::TOP_LEVEL
if (($importer["uid"] == 0) && ($importer["importer_uid"] != 0)) {
Logger::info("Contact ".$importer["id"]." isn't known to user ".$importer["importer_uid"].". The post will be ignored.");
if (($importer['uid'] == 0) && ($importer['importer_uid'] != 0)) {
Logger::info("Contact " . $importer['id'] . " isn't known to user " . $importer['importer_uid'] . ". The post will be ignored.");
return;
}
if (!Strings::compareLink($item["owner-link"], $importer["url"])) {
if (!Strings::compareLink($item['owner-link'], $importer['url'])) {
/*
* The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery,
* but otherwise there's a possible data mixup on the sender's system.
@ -2128,12 +2148,12 @@ class DFRN
* but we're going to unconditionally correct it here so that the post will always be owned by our contact.
*/
Logger::info('Correcting item owner.');
$item["owner-link"] = $importer["url"];
$item["owner-id"] = Contact::getIdForURL($importer["url"], 0);
$item['owner-link'] = $importer['url'];
$item['owner-id'] = Contact::getIdForURL($importer['url'], 0);
}
if (($importer["rel"] == Contact::FOLLOWER) && (!self::tgroupCheck($importer["importer_uid"], $item))) {
Logger::info("Contact ".$importer["id"]." is only follower and tgroup check was negative.");
if (($importer['rel'] == Contact::FOLLOWER) && (!self::tgroupCheck($importer['importer_uid'], $item))) {
Logger::info("Contact " . $importer['id'] . " is only follower and tgroup check was negative.");
return;
}
@ -2147,13 +2167,13 @@ class DFRN
$posted_id = $notify;
}
Logger::info("Item was stored with id ".$posted_id);
Logger::info("Item was stored with id " . $posted_id);
if ($item['uid'] == 0) {
Item::distribute($posted_id);
}
if (stristr($item["verb"], Activity::POKE)) {
if (stristr($item['verb'], Activity::POKE)) {
$item['id'] = $posted_id;
self::doPoke($item, $importer);
}
@ -2163,56 +2183,55 @@ class DFRN
/**
* Deletes items
*
* @param object $xpath XPath object
* @param object $deletion deletion elements
* @param array $importer Record of the importer user mixed with contact of the content
* @param DOMXPath $xpath XPath object
* @param DOMNode $deletion deletion elements
* @param array $importer Record of the importer user mixed with contact of the content
* @return void
* @throws \Exception
* @todo set proper type-hints
*/
private static function processDeletion($xpath, $deletion, array $importer)
private static function processDeletion(DOMXPath $xpath, DOMNode $deletion, array $importer)
{
Logger::notice("Processing deletions");
$uri = null;
foreach ($deletion->attributes as $attributes) {
if ($attributes->name == "ref") {
if ($attributes->name == 'ref') {
$uri = $attributes->textContent;
}
}
if (!$uri || !$importer["id"]) {
if (!$uri || !$importer['id']) {
return false;
}
$condition = ['uri' => $uri, 'uid' => $importer["importer_uid"]];
$condition = ['uri' => $uri, 'uid' => $importer['importer_uid']];
$item = Post::selectFirst(['id', 'parent', 'contact-id', 'uri-id', 'deleted', 'gravity'], $condition);
if (!DBA::isResult($item)) {
Logger::info("Item with uri " . $uri . " for user " . $importer["importer_uid"] . " wasn't found.");
Logger::info('Item with URI ' . $uri . ' for user ' . $importer['importer_uid'] . ' was not found.');
return;
}
if (DBA::exists('post-category', ['uri-id' => $item['uri-id'], 'uid' => $importer['importer_uid'], 'type' => Post\Category::FILE])) {
Logger::notice("Item is filed. It won't be deleted.", ['uri' => $uri, 'uri-id' => $item['uri_id'], 'uid' => $importer["importer_uid"]]);
Logger::notice('Item is filed. It will not be deleted.', ['uri' => $uri, 'uri-id' => $item['uri_id'], 'uid' => $importer['importer_uid']]);
return;
}
// When it is a starting post it has to belong to the person that wants to delete it
if (($item['gravity'] == GRAVITY_PARENT) && ($item['contact-id'] != $importer["id"])) {
Logger::info("Item with uri " . $uri . " don't belong to contact " . $importer["id"] . " - ignoring deletion.");
if (($item['gravity'] == GRAVITY_PARENT) && ($item['contact-id'] != $importer['id'])) {
Logger::info('Item with URI ' . $uri . ' do not belong to contact ' . $importer['id'] . ' - ignoring deletion.');
return;
}
// Comments can be deleted by the thread owner or comment owner
if (($item['gravity'] != GRAVITY_PARENT) && ($item['contact-id'] != $importer["id"])) {
$condition = ['id' => $item['parent'], 'contact-id' => $importer["id"]];
if (($item['gravity'] != GRAVITY_PARENT) && ($item['contact-id'] != $importer['id'])) {
$condition = ['id' => $item['parent'], 'contact-id' => $importer['id']];
if (!Post::exists($condition)) {
Logger::info("Item with uri " . $uri . " wasn't found or mustn't be deleted by contact " . $importer["id"] . " - ignoring deletion.");
Logger::info('Item with URI ' . $uri . ' was not found or must not be deleted by contact ' . $importer['id'] . ' - ignoring deletion.');
return;
}
}
if ($item["deleted"]) {
if ($item['deleted']) {
return;
}
@ -2234,7 +2253,7 @@ class DFRN
*/
public static function import(string $xml, array $importer, int $protocol, int $direction): int
{
if ($xml == "") {
if ($xml == '') {
return 400;
}
@ -2242,24 +2261,24 @@ class DFRN
@$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$xpath->registerNamespace("atom", ActivityNamespace::ATOM1);
$xpath->registerNamespace("thr", ActivityNamespace::THREAD);
$xpath->registerNamespace("at", ActivityNamespace::TOMB);
$xpath->registerNamespace("media", ActivityNamespace::MEDIA);
$xpath->registerNamespace("dfrn", ActivityNamespace::DFRN);
$xpath->registerNamespace("activity", ActivityNamespace::ACTIVITY);
$xpath->registerNamespace("georss", ActivityNamespace::GEORSS);
$xpath->registerNamespace("poco", ActivityNamespace::POCO);
$xpath->registerNamespace("ostatus", ActivityNamespace::OSTATUS);
$xpath->registerNamespace("statusnet", ActivityNamespace::STATUSNET);
$xpath->registerNamespace('atom', ActivityNamespace::ATOM1);
$xpath->registerNamespace('thr', ActivityNamespace::THREAD);
$xpath->registerNamespace('at', ActivityNamespace::TOMB);
$xpath->registerNamespace('media', ActivityNamespace::MEDIA);
$xpath->registerNamespace('dfrn', ActivityNamespace::DFRN);
$xpath->registerNamespace('activity', ActivityNamespace::ACTIVITY);
$xpath->registerNamespace('georss', ActivityNamespace::GEORSS);
$xpath->registerNamespace('poco', ActivityNamespace::POCO);
$xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS);
$xpath->registerNamespace('statusnet', ActivityNamespace::STATUSNET);
$header = [];
$header["uid"] = $importer["importer_uid"];
$header["network"] = Protocol::DFRN;
$header["wall"] = 0;
$header["origin"] = 0;
$header["contact-id"] = $importer["id"];
$header["direction"] = $direction;
$header['uid'] = $importer['importer_uid'];
$header['network'] = Protocol::DFRN;
$header['wall'] = 0;
$header['origin'] = 0;
$header['contact-id'] = $importer['id'];
$header['direction'] = $direction;
if ($direction === Conversation::RELAY) {
$header['post-reason'] = Item::PR_RELAY;
@ -2268,31 +2287,31 @@ class DFRN
// Update the contact table if the data has changed
// The "atom:author" is only present in feeds
if ($xpath->query("/atom:feed/atom:author")->length > 0) {
self::fetchauthor($xpath, $doc->firstChild, $importer, "atom:author", false, $xml);
if ($xpath->query('/atom:feed/atom:author')->length > 0) {
self::fetchauthor($xpath, $doc->firstChild, $importer, 'atom:author', false, $xml);
}
// Only the "dfrn:owner" in the head section contains all data
if ($xpath->query("/atom:feed/dfrn:owner")->length > 0) {
self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false, $xml);
if ($xpath->query('/atom:feed/dfrn:owner')->length > 0) {
self::fetchauthor($xpath, $doc->firstChild, $importer, 'dfrn:owner', false, $xml);
}
Logger::info("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"]);
Logger::info("Import DFRN message for user " . $importer['importer_uid'] . " from contact " . $importer['id']);
if (!empty($importer['gsid']) && ($protocol == Conversation::PARCEL_DIASPORA_DFRN)) {
GServer::setProtocol($importer['gsid'], Post\DeliveryData::DFRN);
}
// is it a public forum? Private forums aren't exposed with this method
$forum = intval(XML::getFirstNodeValue($xpath, "/atom:feed/dfrn:community/text()"));
$forum = intval(XML::getFirstNodeValue($xpath, '/atom:feed/dfrn:community/text()'));
// The account type is new since 3.5.1
if ($xpath->query("/atom:feed/dfrn:account_type")->length > 0) {
if ($xpath->query('/atom:feed/dfrn:account_type')->length > 0) {
// Hint: We are using separate update calls for uid=0 and uid!=0 since a combined call is bad for the database performance
$accounttype = intval(XML::getFirstNodeValue($xpath, "/atom:feed/dfrn:account_type/text()"));
$accounttype = intval(XML::getFirstNodeValue($xpath, '/atom:feed/dfrn:account_type/text()'));
if ($accounttype != $importer["contact-type"]) {
if ($accounttype != $importer['contact-type']) {
Contact::update(['contact-type' => $accounttype], ['id' => $importer['id']]);
// Updating the public contact as well
@ -2316,8 +2335,8 @@ class DFRN
$condition = ['(`forum` OR `prv`) AND `uid` = 0 AND `nurl` = ?', $importer['nurl']];
Contact::update(['forum' => false, 'prv' => false], $condition);
}
} elseif ($forum != $importer["forum"]) { // Deprecated since 3.5.1
$condition = ['`forum` != ? AND `id` = ?', $forum, $importer["id"]];
} elseif ($forum != $importer['forum']) { // Deprecated since 3.5.1
$condition = ['`forum` != ? AND `id` = ?', $forum, $importer['id']];
Contact::update(['forum' => $forum], $condition);
// Updating the public contact as well
@ -2327,40 +2346,40 @@ class DFRN
// We are processing relocations even if we are ignoring a contact
$relocations = $xpath->query("/atom:feed/dfrn:relocate");
$relocations = $xpath->query('/atom:feed/dfrn:relocate');
foreach ($relocations as $relocation) {
self::processRelocation($xpath, $relocation, $importer);
}
if (($importer["uid"] != 0) && !$importer["readonly"]) {
$mails = $xpath->query("/atom:feed/dfrn:mail");
if (($importer['uid'] != 0) && !$importer['readonly']) {
$mails = $xpath->query('/atom:feed/dfrn:mail');
foreach ($mails as $mail) {
self::processMail($xpath, $mail, $importer);
}
$suggestions = $xpath->query("/atom:feed/dfrn:suggest");
$suggestions = $xpath->query('/atom:feed/dfrn:suggest');
foreach ($suggestions as $suggestion) {
self::processSuggestion($xpath, $suggestion, $importer);
}
}
$deletions = $xpath->query("/atom:feed/at:deleted-entry");
$deletions = $xpath->query('/atom:feed/at:deleted-entry');
if (!empty($deletions)) {
foreach ($deletions as $deletion) {
self::processDeletion($xpath, $deletion, $importer);
}
if (count($deletions) > 0) {
Logger::notice('Deletions had been processed');
Logger::notice(count($deletions) . ' deletions had been processed');
return 200;
}
}
$entries = $xpath->query("/atom:feed/atom:entry");
$entries = $xpath->query('/atom:feed/atom:entry');
foreach ($entries as $entry) {
self::processEntry($header, $xpath, $entry, $importer, $xml, $protocol);
}
Logger::info("Import done for user " . $importer["importer_uid"] . " from contact " . $importer["id"]);
Logger::info("Import done for user " . $importer['importer_uid'] . " from contact " . $importer['id']);
return 200;
}

View file

@ -38,10 +38,10 @@ class Email
* @param string $mailbox The mailbox name
* @param string $username The username
* @param string $password The password
* @return Connection|resource
* @return Connection|resource|bool
* @throws \Exception
*/
public static function connect($mailbox, $username, $password)
public static function connect(string $mailbox, string $username, string $password)
{
if (!function_exists('imap_open')) {
return false;
@ -68,7 +68,7 @@ class Email
* @return array
* @throws \Exception
*/
public static function poll($mbox, $email_addr): array
public static function poll($mbox, string $email_addr): array
{
if (!$mbox || !$email_addr) {
return [];
@ -101,10 +101,12 @@ class Email
}
/**
* Returns mailbox name
*
* @param array $mailacct mail account
* @return string
*/
public static function constructMailboxName($mailacct)
public static function constructMailboxName(array $mailacct): string
{
$ret = '{' . $mailacct['server'] . ((intval($mailacct['port'])) ? ':' . $mailacct['port'] : '');
$ret .= (($mailacct['ssltype']) ? '/' . $mailacct['ssltype'] . '/novalidate-cert' : '');
@ -117,7 +119,7 @@ class Email
* @param integer $uid user id
* @return mixed
*/
public static function messageMeta($mbox, $uid)
public static function messageMeta($mbox, int $uid)
{
$ret = (($mbox && $uid) ? @imap_fetch_overview($mbox, $uid, FT_UID) : [[]]); // POSSIBLE CLEANUP --> array(array()) is probably redundant now
return (count($ret)) ? $ret : [];
@ -127,10 +129,11 @@ class Email
* @param Connection|resource $mbox mailbox
* @param integer $uid user id
* @param string $reply reply
* @param array $item Item
* @return array
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getMessage($mbox, $uid, $reply, $item): array
public static function getMessage($mbox, int $uid, string $reply, array $item): array
{
$ret = $item;
@ -218,7 +221,7 @@ class Email
* @param string $subtype sub type
* @return string
*/
private static function messageGetPart($mbox, $uid, $p, $partno, $subtype)
private static function messageGetPart($mbox, int $uid, $p, in $partno, string $subtype): string
{
// $partno = '1', '2', '2.1', '2.1.3', etc for multipart, 0 if simple
global $htmlmsg,$plainmsg,$charset,$attachments;
@ -296,11 +299,13 @@ class Email
}
/**
* Returns encoded header
*
* @param string $in_str in string
* @param string $charset character set
* @return string
*/
public static function encodeHeader($in_str, $charset)
public static function encodeHeader(string $in_str, string $charset): string
{
$out_str = $in_str;
$need_to_convert = false;
@ -360,21 +365,20 @@ class Email
* @param string $subject subject
* @param string $headers headers
* @param array $item item
*
* @return void
* @return bool Status from mail()
*
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
* @todo This could be changed to use the Emailer class
*/
public static function send($addr, $subject, $headers, $item)
public static function send(string $addr, string $subject, string $headers, array$item)
{
//$headers .= 'MIME-Version: 1.0' . "\n";
//$headers .= 'Content-Type: text/html; charset=UTF-8' . "\n";
//$headers .= 'Content-Type: text/plain; charset=UTF-8' . "\n";
//$headers .= 'Content-Transfer-Encoding: 8bit' . "\n\n";
$part = uniqid("", true);
$part = uniqid('', true);
$html = Item::prepareBody($item);
@ -398,52 +402,70 @@ class Email
//$message = '<html><body>' . $html . '</body></html>';
//$message = html2plain($html);
Logger::notice('notifier: email delivery to ' . $addr);
mail($addr, $subject, $body, $headers);
return mail($addr, $subject, $body, $headers);
}
/**
* @param string $iri string
* @return string
* Convert item URI to message id
*
* @param string $itemUri Item URI
* @return string Message id
*/
public static function iri2msgid($iri)
public static function iri2msgid(string $itemUri): string
{
if (!strpos($iri, "@")) {
$msgid = preg_replace("/urn:(\S+):(\S+)\.(\S+):(\d+):(\S+)/i", "urn!$1!$4!$5@$2.$3", $iri);
} else {
$msgid = $iri;
$msgid = $itemUri;
if (!strpos($itemUri, '@')) {
$msgid = preg_replace("/urn:(\S+):(\S+)\.(\S+):(\d+):(\S+)/i", "urn!$1!$4!$5@$2.$3", $itemUri);
}
return $msgid;
}
/**
* @param string $msgid msgid
* @return string
* Converts message id to item URI
*
* @param string $msgid Message id
* @return string Item URI
*/
public static function msgid2iri($msgid)
public static function msgid2iri(string $msgid): string
{
if (strpos($msgid, "@")) {
$iri = preg_replace("/urn!(\S+)!(\d+)!(\S+)@(\S+)\.(\S+)/i", "urn:$1:$4.$5:$2:$3", $msgid);
} else {
$iri = $msgid;
$itemUri = $msgid;
if (strpos($msgid, '@')) {
$itemUri = preg_replace("/urn!(\S+)!(\d+)!(\S+)@(\S+)\.(\S+)/i", "urn:$1:$4.$5:$2:$3", $msgid);
}
return $iri;
return $itemUri;
}
private static function saveReplace($pattern, $replace, $text)
/**
* Invokes preg_replace() but does return full text from parameter if it
* returned an empty message.
*
* @param string $pattern Pattern to match
* @param string $replace String to replace with
* @param string $text String to check
* @return string Replaced string
*/
private static function saveReplace(string $pattern, string $replace, string $text): string
{
$save = $text;
$return = preg_replace($pattern, $replace, $text);
$text = preg_replace($pattern, $replace, $text);
if ($text == '') {
$text = $save;
if ($return == '') {
$return = $text;
}
return $text;
return $return;
}
private static function unifyAttributionLine($message)
/**
* Unifies attribution line(s)
*
* @param string $message Unfiltered message
* @return string Message with unified attribution line(s)
*/
private static function unifyAttributionLine(string $message): string
{
$quotestr = ['quote', 'spoiler'];
foreach ($quotestr as $quote) {
@ -520,7 +542,13 @@ class Email
return $message;
}
private static function removeGPG($message)
/**
* Removes GPG part from message
*
* @param string $message Unfiltered message
* @return string Message with GPG part
*/
private static function removeGPG(string $message): string
{
$pattern = '/(.*)\s*-----BEGIN PGP SIGNED MESSAGE-----\s*[\r\n].*Hash:.*?[\r\n](.*)'.
'[\r\n]\s*-----BEGIN PGP SIGNATURE-----\s*[\r\n].*'.
@ -537,7 +565,13 @@ class Email
return $cleaned;
}
private static function removeSig($message)
/**
* Removes signature from message
*
* @param string $message Unfiltered message
* @return string Message with no signature
*/
private static function removeSig(string $message): string
{
$sigpos = strrpos($message, "\n-- \n");
$quotepos = strrpos($message, "[/quote]");
@ -569,7 +603,13 @@ class Email
return ['body' => $cleaned, 'sig' => $sig];
}
private static function removeLinebreak($message)
/**
* Removes lines breaks from message
*
* @param string $message Unfiltered message
* @return string Message with no line breaks
*/
private static function removeLinebreak(string $message): string
{
$arrbody = explode("\n", trim($message));
@ -622,7 +662,7 @@ class Email
return implode("\n", $lines);
}
private static function convertQuote($body, $reply)
private static function convertQuote(strng $body, string $reply): string
{
// Convert Quotes
$arrbody = explode("\n", trim($body));
@ -682,14 +722,14 @@ class Email
return $body;
}
private static function removeToFu($message)
private static function removeToFu(string $message): string
{
$message = trim($message);
do {
$oldmessage = $message;
$message = preg_replace('=\[/quote\][\s](.*?)\[quote\]=i', '$1', $message);
$message = str_replace("[/quote][quote]", "", $message);
$message = str_replace('[/quote][quote]', '', $message);
} while ($message != $oldmessage);
$quotes = [];
@ -724,8 +764,9 @@ class Email
$start = $pos + 7;
}
if (strtolower(substr($message, -8)) != '[/quote]')
if (strtolower(substr($message, -8)) != '[/quote]') {
return($message);
}
krsort($quotes);
@ -739,7 +780,7 @@ class Email
}
if ($quotestart != 0) {
$message = trim(substr($message, 0, $quotestart))."\n[spoiler]".substr($message, $quotestart+7, -8).'[/spoiler]';
$message = trim(substr($message, 0, $quotestart))."\n[spoiler]".substr($message, $quotestart+7, -8) . '[/spoiler]';
}
return $message;

View file

@ -22,6 +22,7 @@
namespace Friendica\Protocol;
use DOMDocument;
use DOMElement;
use DOMXPath;
use Friendica\Content\PageInfo;
use Friendica\Content\Text\BBCode;
@ -66,7 +67,7 @@ class Feed
if ($dryRun) {
Logger::info("Test Atom/RSS feed");
} else {
Logger::info("Import Atom/RSS feed '" . $contact["name"] . "' (Contact " . $contact["id"] . ") for user " . $importer["uid"]);
Logger::info('Import Atom/RSS feed "' . $contact['name'] . '" (Contact ' . $contact['id'] . ') for user ' . $importer['uid']);
}
$xml = trim($xml);
@ -378,7 +379,7 @@ class Feed
if (DBA::isResult($previous)) {
// Use the creation date when the post had been stored. It can happen this date changes in the feed.
$creation_dates[] = $previous['created'];
Logger::info("Item with uri " . $item["uri"] . " for user " . $importer["uid"] . " already existed under id " . $previous["id"]);
Logger::info('Item with URI ' . $item['uri'] . ' for user ' . $importer['uid'] . ' already existed under id ' . $previous['id']);
continue;
}
$creation_dates[] = DateTimeFormat::utc($item['created']);
@ -683,7 +684,7 @@ class Feed
/**
* Automatically adjust the poll frequency according to the post frequency
*
* @param array $contact
* @param array $contact Contact array
* @param array $creation_dates
* @return void
*/
@ -803,7 +804,7 @@ class Feed
* @param array $contact
* @return int Poll interval in minutes
*/
public static function getPollInterval(array $contact)
public static function getPollInterval(array $contact): int
{
if (in_array($contact['network'], [Protocol::MAIL, Protocol::FEED])) {
$ratings = [0, 3, 7, 8, 9, 10];
@ -852,39 +853,39 @@ class Feed
* @param array $tags
* @return string tag string
*/
private static function tagToString(array $tags)
private static function tagToString(array $tags): string
{
$tagstr = '';
foreach ($tags as $tag) {
if ($tagstr != "") {
$tagstr .= ", ";
if ($tagstr != '') {
$tagstr .= ', ';
}
$tagstr .= "#[url=" . DI::baseUrl() . "/search?tag=" . urlencode($tag) . "]" . $tag . "[/url]";
$tagstr .= '#[url=' . DI::baseUrl() . '/search?tag=' . urlencode($tag) . ']' . $tag . '[/url]';
}
return $tagstr;
}
private static function titleIsBody($title, $body)
private static function titleIsBody(string $title, string $body): bool
{
$title = strip_tags($title);
$title = trim($title);
$title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
$title = str_replace(["\n", "\r", "\t", " "], ["", "", "", ""], $title);
$title = str_replace(["\n", "\r", "\t", " "], ['', '', '', ''], $title);
$body = strip_tags($body);
$body = trim($body);
$body = html_entity_decode($body, ENT_QUOTES, 'UTF-8');
$body = str_replace(["\n", "\r", "\t", " "], ["", "", "", ""], $body);
$body = str_replace(["\n", "\r", "\t", " "], ['', '', '', ''], $body);
if (strlen($title) < strlen($body)) {
$body = substr($body, 0, strlen($title));
}
if (($title != $body) && (substr($title, -3) == "...")) {
$pos = strrpos($title, "...");
if (($title != $body) && (substr($title, -3) == '...')) {
$pos = strrpos($title, '...');
if ($pos > 0) {
$title = substr($title, 0, $pos);
$body = substr($body, 0, $pos);
@ -914,7 +915,7 @@ class Feed
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function atom($owner_nick, $last_update, $max_items = 300, $filter = 'activity', $nocache = false)
public static function atom(string $owner_nick, string $last_update, int $max_items = 300, string $filter = 'activity', bool $nocache = false)
{
$stamp = microtime(true);
@ -923,7 +924,7 @@ class Feed
return;
}
$cachekey = "feed:feed:" . $owner_nick . ":" . $filter . ":" . $last_update;
$cachekey = 'feed:feed:' . $owner_nick . ':' . $filter . ':' . $last_update;
// Display events in the users's timezone
if (strlen($owner['timezone'])) {
@ -942,11 +943,11 @@ class Feed
}
$check_date = empty($last_update) ? '' : DateTimeFormat::utc($last_update);
$authorid = Contact::getIdForURL($owner["url"]);
$authorid = Contact::getIdForURL($owner['url']);
$condition = ["`uid` = ? AND `received` > ? AND NOT `deleted` AND `gravity` IN (?, ?)
AND `private` != ? AND `visible` AND `wall` AND `parent-network` IN (?, ?, ?, ?)",
$owner["uid"], $check_date, GRAVITY_PARENT, GRAVITY_COMMENT,
$owner['uid'], $check_date, GRAVITY_PARENT, GRAVITY_COMMENT,
Item::PRIVATE, Protocol::ACTIVITYPUB,
Protocol::OSTATUS, Protocol::DFRN, Protocol::DIASPORA];
@ -957,7 +958,7 @@ class Feed
if ($owner['account-type'] != User::ACCOUNT_TYPE_COMMUNITY) {
$condition[0] .= " AND `contact-id` = ? AND `author-id` = ?";
$condition[] = $owner["id"];
$condition[] = $owner['id'];
$condition[] = $authorid;
}
@ -1002,16 +1003,16 @@ class Feed
* @param array $owner Contact data of the poster
* @param string $filter The related feed filter (activity, posts or comments)
*
* @return object header root element
* @return DOMElement Header root element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function addHeader(DOMDocument $doc, array $owner, $filter)
private static function addHeader(DOMDocument $doc, array $owner, string $filter): DOMElement
{
$root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed');
$doc->appendChild($root);
$title = '';
$selfUri = '/feed/' . $owner["nick"] . '/';
$selfUri = '/feed/' . $owner['nick'] . '/';
switch ($filter) {
case 'activity':
$title = DI::l10n()->t('%s\'s timeline', $owner['name']);
@ -1026,24 +1027,24 @@ class Feed
break;
}
$attributes = ["uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION . "-" . DB_UPDATE_VERSION];
XML::addElement($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes);
XML::addElement($doc, $root, "id", DI::baseUrl() . "/profile/" . $owner["nick"]);
XML::addElement($doc, $root, "title", $title);
XML::addElement($doc, $root, "subtitle", sprintf("Updates from %s on %s", $owner["name"], DI::config()->get('config', 'sitename')));
XML::addElement($doc, $root, "logo", User::getAvatarUrl($owner, Proxy::SIZE_SMALL));
XML::addElement($doc, $root, "updated", DateTimeFormat::utcNow(DateTimeFormat::ATOM));
$attributes = ['uri' => 'https://friendi.ca', 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION];
XML::addElement($doc, $root, 'generator', FRIENDICA_PLATFORM, $attributes);
XML::addElement($doc, $root, 'id', DI::baseUrl() . '/profile/' . $owner['nick']);
XML::addElement($doc, $root, 'title', $title);
XML::addElement($doc, $root, 'subtitle', sprintf("Updates from %s on %s", $owner['name'], DI::config()->get('config', 'sitename')));
XML::addElement($doc, $root, 'logo', User::getAvatarUrl($owner, Proxy::SIZE_SMALL));
XML::addElement($doc, $root, 'updated', DateTimeFormat::utcNow(DateTimeFormat::ATOM));
$author = self::addAuthor($doc, $owner);
$root->appendChild($author);
$attributes = ["href" => $owner["url"], "rel" => "alternate", "type" => "text/html"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['href' => $owner['url'], 'rel' => 'alternate', 'type' => 'text/html'];
XML::addElement($doc, $root, 'link', '', $attributes);
OStatus::hublinks($doc, $root, $owner["nick"]);
OStatus::addHubLink($doc, $root, $owner['nick']);
$attributes = ["href" => DI::baseUrl() . $selfUri, "rel" => "self", "type" => "application/atom+xml"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['href' => DI::baseUrl() . $selfUri, 'rel' => 'self', 'type' => 'application/atom+xml'];
XML::addElement($doc, $root, 'link', '', $attributes);
return $root;
}
@ -1053,16 +1054,15 @@ class Feed
*
* @param DOMDocument $doc XML document
* @param array $owner Contact data of the poster
*
* @return \DOMElement author element
* @return DOMElement author element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function addAuthor(DOMDocument $doc, array $owner)
private static function addAuthor(DOMDocument $doc, array $owner): DOMElement
{
$author = $doc->createElement("author");
XML::addElement($doc, $author, "uri", $owner["url"]);
XML::addElement($doc, $author, "name", $owner["nick"]);
XML::addElement($doc, $author, "email", $owner["addr"]);
$author = $doc->createElement('author');
XML::addElement($doc, $author, 'uri', $owner['url']);
XML::addElement($doc, $author, 'name', $owner['nick']);
XML::addElement($doc, $author, 'email', $owner['addr']);
return $author;
}
@ -1074,15 +1074,14 @@ class Feed
* @param array $item Data of the item that is to be posted
* @param array $owner Contact data of the poster
* @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
*
* @return \DOMElement Entry element
* @return DOMElement Entry element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function noteEntry(DOMDocument $doc, array $item, array $owner)
private static function noteEntry(DOMDocument $doc, array $item, array $owner): DOMElement
{
if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) {
Logger::info('Feed entry author does not match feed owner', ['owner' => $owner["url"], 'author' => $item["author-link"]]);
if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
Logger::info('Feed entry author does not match feed owner', ['owner' => $owner['url'], 'author' => $item['author-link']]);
}
$entry = OStatus::entryHeader($doc, $owner, $item, false);
@ -1104,31 +1103,30 @@ class Feed
* @param string $title Title for the post
* @param string $verb The activity verb
* @param bool $complete Add the "status_net" element?
* @param bool $feed_mode Behave like a regular feed for users if true
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function entryContent(DOMDocument $doc, \DOMElement $entry, array $item, $title, $verb = "", $complete = true)
private static function entryContent(DOMDocument $doc, DOMElement $entry, array $item, $title, string $verb = '', bool $complete = true)
{
if ($verb == "") {
if ($verb == '') {
$verb = OStatus::constructVerb($item);
}
XML::addElement($doc, $entry, "id", $item["uri"]);
XML::addElement($doc, $entry, "title", html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
XML::addElement($doc, $entry, 'id', $item['uri']);
XML::addElement($doc, $entry, 'title', html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
$body = OStatus::formatPicturePost($item['body'], $item['uri-id']);
$body = BBCode::convertForUriId($item['uri-id'], $body, BBCode::ACTIVITYPUB);
XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);
XML::addElement($doc, $entry, 'content', $body, ['type' => 'html']);
XML::addElement($doc, $entry, "link", "", ["rel" => "alternate", "type" => "text/html",
"href" => DI::baseUrl()."/display/".$item["guid"]]
XML::addElement($doc, $entry, 'link', '', ['rel' => 'alternate', 'type' => 'text/html',
'href' => DI::baseUrl() . '/display/' . $item['guid']]
);
XML::addElement($doc, $entry, "published", DateTimeFormat::utc($item["created"]."+00:00", DateTimeFormat::ATOM));
XML::addElement($doc, $entry, "updated", DateTimeFormat::utc($item["edited"]."+00:00", DateTimeFormat::ATOM));
XML::addElement($doc, $entry, 'published', DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM));
XML::addElement($doc, $entry, 'updated', DateTimeFormat::utc($item['edited'] . '+00:00', DateTimeFormat::ATOM));
}
/**
@ -1197,28 +1195,28 @@ class Feed
* @param array $item
* @return string title
*/
private static function getTitle(array $item)
private static function getTitle(array $item): string
{
if ($item['title'] != '') {
return BBCode::convertForUriId($item['uri-id'], $item['title'], BBCode::ACTIVITYPUB);
}
// Fetch information about the post
$siteinfo = BBCode::getAttachedData($item["body"]);
if (isset($siteinfo["title"])) {
return $siteinfo["title"];
$siteinfo = BBCode::getAttachedData($item['body']);
if (isset($siteinfo['title'])) {
return $siteinfo['title'];
}
// If no bookmark is found then take the first line
// Remove the share element before fetching the first line
$title = trim(preg_replace("/\[share.*?\](.*?)\[\/share\]/ism","\n$1\n",$item['body']));
$title = trim(preg_replace("/\[share.*?\](.*?)\[\/share\]/ism", "\n$1\n", $item['body']));
$title = BBCode::toPlaintext($title)."\n";
$pos = strpos($title, "\n");
$trailer = "";
$trailer = '';
if (($pos == 0) || ($pos > 100)) {
$pos = 100;
$trailer = "...";
$trailer = '...';
}
return substr($title, 0, $pos) . $trailer;

View file

@ -22,6 +22,7 @@
namespace Friendica\Protocol;
use DOMDocument;
use DOMElement;
use DOMXPath;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
@ -67,24 +68,24 @@ class OStatus
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function fetchAuthor(DOMXPath $xpath, $context, array $importer, array &$contact = null, $onlyfetch)
private static function fetchAuthor(DOMXPath $xpath, $context, array $importer, array &$contact = null, bool $onlyfetch): array
{
$author = [];
$author["author-link"] = XML::getFirstNodeValue($xpath, 'atom:author/atom:uri/text()', $context);
$author["author-name"] = XML::getFirstNodeValue($xpath, 'atom:author/atom:name/text()', $context);
$author['author-link'] = XML::getFirstNodeValue($xpath, 'atom:author/atom:uri/text()', $context);
$author['author-name'] = XML::getFirstNodeValue($xpath, 'atom:author/atom:name/text()', $context);
$addr = XML::getFirstNodeValue($xpath, 'atom:author/atom:email/text()', $context);
$aliaslink = $author["author-link"];
$aliaslink = $author['author-link'];
$alternate_item = $xpath->query("atom:author/atom:link[@rel='alternate']", $context)->item(0);
if (is_object($alternate_item)) {
foreach ($alternate_item->attributes as $attributes) {
if (($attributes->name == "href") && ($attributes->textContent != "")) {
$author["author-link"] = $attributes->textContent;
if (($attributes->name == 'href') && ($attributes->textContent != '')) {
$author['author-link'] = $attributes->textContent;
}
}
}
$author["author-id"] = Contact::getIdForURL($author["author-link"]);
$author['author-id'] = Contact::getIdForURL($author['author-link']);
$author['contact-id'] = ($contact['id'] ?? 0) ?: $author['author-id'];
@ -94,34 +95,45 @@ class OStatus
This here would be better, but we would get problems with contacts from the statusnet addon
This is kept here as a reminder for the future
$cid = Contact::getIdForURL($author["author-link"], $importer["uid"]);
$cid = Contact::getIdForURL($author['author-link'], $importer['uid']);
if ($cid) {
$contact = DBA::selectFirst('contact', [], ['id' => $cid]);
}
*/
if ($aliaslink != '') {
$condition = ["`uid` = ? AND `alias` = ? AND `network` != ? AND `rel` IN (?, ?)",
$importer["uid"], $aliaslink, Protocol::STATUSNET,
Contact::SHARING, Contact::FRIEND];
$contact = DBA::selectFirst('contact', [], $condition);
$contact = DBA::selectFirst('contact', [], [
"`uid` = ? AND `alias` = ? AND `network` != ? AND `rel` IN (?, ?)",
$importer['uid'],
$aliaslink, Protocol::STATUSNET,
Contact::SHARING, Contact::FRIEND,
]);
}
if (!DBA::isResult($contact) && $author["author-link"] != '') {
if ($aliaslink == "") {
$aliaslink = $author["author-link"];
if (!DBA::isResult($contact) && $author['author-link'] != '') {
if ($aliaslink == '') {
$aliaslink = $author['author-link'];
}
$condition = ["`uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?)",
$importer["uid"], Strings::normaliseLink($author["author-link"]), Strings::normaliseLink($aliaslink),
Protocol::STATUSNET, Contact::SHARING, Contact::FRIEND];
$contact = DBA::selectFirst('contact', [], $condition);
$contact = DBA::selectFirst('contact', [], [
"`uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?)",
$importer['uid'],
Strings::normaliseLink($author['author-link']),
Strings::normaliseLink($aliaslink),
Protocol::STATUSNET,
Contact::SHARING,
Contact::FRIEND,
]);
}
if (!DBA::isResult($contact) && ($addr != '')) {
$condition = ["`uid` = ? AND `addr` = ? AND `network` != ? AND `rel` IN (?, ?)",
$importer["uid"], $addr, Protocol::STATUSNET,
Contact::SHARING, Contact::FRIEND];
$contact = DBA::selectFirst('contact', [], $condition);
$contact = DBA::selectFirst('contact', [], [
"`uid` = ? AND `addr` = ? AND `network` != ? AND `rel` IN (?, ?)",
$importer['uid'],
$addr,
Protocol::STATUSNET,
Contact::SHARING,
Contact::FRIEND,
]);
}
if (DBA::isResult($contact)) {
@ -130,40 +142,40 @@ class OStatus
} elseif (!empty(APContact::getByURL($contact['url'], false))) {
ActivityPub\Receiver::switchContact($contact['id'], $importer['uid'], $contact['url']);
}
$author["contact-id"] = $contact["id"];
$author['contact-id'] = $contact['id'];
}
$avatarlist = [];
$avatars = $xpath->query("atom:author/atom:link[@rel='avatar']", $context);
foreach ($avatars as $avatar) {
$href = "";
$href = '';
$width = 0;
foreach ($avatar->attributes as $attributes) {
if ($attributes->name == "href") {
if ($attributes->name == 'href') {
$href = $attributes->textContent;
}
if ($attributes->name == "width") {
if ($attributes->name == 'width') {
$width = $attributes->textContent;
}
}
if ($href != "") {
if ($href != '') {
$avatarlist[$width] = $href;
}
}
if (count($avatarlist) > 0) {
krsort($avatarlist);
$author["author-avatar"] = Probe::fixAvatar(current($avatarlist), $author["author-link"]);
$author['author-avatar'] = Probe::fixAvatar(current($avatarlist), $author['author-link']);
}
$displayname = XML::getFirstNodeValue($xpath, 'atom:author/poco:displayName/text()', $context);
if ($displayname != "") {
$author["author-name"] = $displayname;
if ($displayname != '') {
$author['author-name'] = $displayname;
}
$author["owner-id"] = $author["author-id"];
$author['owner-id'] = $author['author-id'];
// Only update the contacts if it is an OStatus contact
if (DBA::isResult($contact) && ($contact['id'] > 0) && !$onlyfetch && ($contact["network"] == Protocol::OSTATUS)) {
if (DBA::isResult($contact) && ($contact['id'] > 0) && !$onlyfetch && ($contact['network'] == Protocol::OSTATUS)) {
// Update contact data
$current = $contact;
@ -179,41 +191,41 @@ class OStatus
// if ($value != "")
// $contact["poll"] = $value;
$contact['url'] = $author["author-link"];
$contact['url'] = $author['author-link'];
$contact['nurl'] = Strings::normaliseLink($contact['url']);
$value = XML::getFirstNodeValue($xpath, 'atom:author/atom:uri/text()', $context);
if ($value != "") {
$contact["alias"] = $value;
if ($value != '') {
$contact['alias'] = $value;
}
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:displayName/text()', $context);
if ($value != "") {
$contact["name"] = $value;
if ($value != '') {
$contact['name'] = $value;
}
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:preferredUsername/text()', $context);
if ($value != "") {
$contact["nick"] = $value;
if ($value != '') {
$contact['nick'] = $value;
}
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:note/text()', $context);
if ($value != "") {
$contact["about"] = HTML::toBBCode($value);
if ($value != '') {
$contact['about'] = HTML::toBBCode($value);
}
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:address/poco:formatted/text()', $context);
if ($value != "") {
$contact["location"] = $value;
if ($value != '') {
$contact['location'] = $value;
}
$contact['name-date'] = DateTimeFormat::utcNow();
Contact::update($contact, ['id' => $contact["id"]], $current);
Contact::update($contact, ['id' => $contact['id']], $current);
if (!empty($author["author-avatar"]) && ($author["author-avatar"] != $current['avatar'])) {
Logger::info("Update profile picture for contact ".$contact["id"]);
Contact::updateAvatar($contact["id"], $author["author-avatar"]);
if (!empty($author['author-avatar']) && ($author['author-avatar'] != $current['avatar'])) {
Logger::info('Update profile picture for contact ' . $contact['id']);
Contact::updateAvatar($contact['id'], $author['author-avatar']);
}
// Ensure that we are having this contact (with uid=0)
@ -224,20 +236,26 @@ class OStatus
$old_contact = DBA::selectFirst('contact', $fields, ['id' => $cid]);
// Update it with the current values
$fields = ['url' => $author["author-link"], 'name' => $contact["name"],
'nurl' => Strings::normaliseLink($author["author-link"]),
'nick' => $contact["nick"], 'alias' => $contact["alias"],
'about' => $contact["about"], 'location' => $contact["location"],
'success_update' => DateTimeFormat::utcNow(), 'last-update' => DateTimeFormat::utcNow()];
$fields = [
'url' => $author['author-link'],
'name' => $contact['name'],
'nurl' => Strings::normaliseLink($author['author-link']),
'nick' => $contact['nick'],
'alias' => $contact['alias'],
'about' => $contact['about'],
'location' => $contact['location'],
'success_update' => DateTimeFormat::utcNow(),
'last-update' => DateTimeFormat::utcNow(),
];
Contact::update($fields, ['id' => $cid], $old_contact);
// Update the avatar
if (!empty($author["author-avatar"])) {
Contact::updateAvatar($cid, $author["author-avatar"]);
if (!empty($author['author-avatar'])) {
Contact::updateAvatar($cid, $author['author-avatar']);
}
}
} elseif (empty($contact["network"]) || ($contact["network"] != Protocol::DFRN)) {
} elseif (empty($contact['network']) || ($contact['network'] != Protocol::DFRN)) {
$contact = [];
}
@ -254,10 +272,10 @@ class OStatus
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function salmonAuthor($xml, array $importer)
public static function salmonAuthor(string $xml, array $importer): array
{
if ($xml == "") {
return;
if (empty($xml)) {
return [];
}
$doc = new DOMDocument();
@ -273,7 +291,7 @@ class OStatus
$xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS);
$xpath->registerNamespace('statusnet', ActivityNamespace::STATUSNET);
$contact = ["id" => 0];
$contact = ['id' => 0];
// Fetch the first author
$authordata = $xpath->query('//author')->item(0);
@ -285,10 +303,9 @@ class OStatus
* Read attributes from element
*
* @param object $element Element object
*
* @return array attributes
*/
private static function readAttributes($element)
private static function readAttributes($element): array
{
$attribute = [];
@ -324,12 +341,12 @@ class OStatus
* @param string $hub Called by reference, returns the fetched hub data
* @param boolean $stored Is the post fresh imported or from the database?
* @param boolean $initialize Is it the leading post so that data has to be initialized?
*
* @param integer $direction Direction, default UNKNOWN(0)
* @return boolean Could the XML be processed?
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function process($xml, array $importer, array &$contact = null, &$hub, $stored = false, $initialize = true, $direction = Conversation::UNKNOWN)
private static function process(string $xml, array $importer, array &$contact = null, string &$hub, bool $stored = false, bool $initialize = true, int $direction = Conversation::UNKNOWN)
{
if ($initialize) {
self::$itemlist = [];
@ -338,9 +355,10 @@ class OStatus
Logger::info('Import OStatus message for user ' . $importer['uid']);
if ($xml == "") {
if (empty($xml)) {
return false;
}
$doc = new DOMDocument();
@$doc->loadXML($xml);
@ -354,26 +372,26 @@ class OStatus
$xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS);
$xpath->registerNamespace('statusnet', ActivityNamespace::STATUSNET);
$hub = "";
$hub = '';
$hub_items = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0);
if (is_object($hub_items)) {
$hub_attributes = $hub_items->attributes;
if (is_object($hub_attributes)) {
foreach ($hub_attributes as $hub_attribute) {
if ($hub_attribute->name == "href") {
if ($hub_attribute->name == 'href') {
$hub = $hub_attribute->textContent;
Logger::info("Found hub ".$hub);
Logger::info('Found hub ', ['hub' => $hub]);
}
}
}
}
$header = [];
$header["uid"] = $importer["uid"];
$header["network"] = Protocol::OSTATUS;
$header["wall"] = 0;
$header["origin"] = 0;
$header["gravity"] = GRAVITY_COMMENT;
$header['uid'] = $importer['uid'];
$header['network'] = Protocol::OSTATUS;
$header['wall'] = 0;
$header['origin'] = 0;
$header['gravity'] = GRAVITY_COMMENT;
if (!is_object($doc->firstChild) || empty($doc->firstChild->tagName)) {
return false;
@ -381,7 +399,7 @@ class OStatus
$first_child = $doc->firstChild->tagName;
if ($first_child == "feed") {
if ($first_child == 'feed') {
$entries = $xpath->query('/atom:feed/atom:entry');
} else {
$entries = $xpath->query('/atom:entry');
@ -395,9 +413,9 @@ class OStatus
$doc2->formatOutput = true;
$xml2 = $doc2->saveXML();
$header["protocol"] = Conversation::PARCEL_SALMON;
$header["source"] = $xml2;
$header["direction"] = $direction;
$header['protocol'] = Conversation::PARCEL_SALMON;
$header['source'] = $xml2;
$header['direction'] = $direction;
} elseif (!$initialize) {
return false;
}
@ -427,67 +445,67 @@ class OStatus
$item = array_merge($header, $author);
$item["uri"] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry);
$item['uri'] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry);
$item['uri-id'] = ItemURI::insert(['uri' => $item['uri']]);
$item["verb"] = XML::getFirstNodeValue($xpath, 'activity:verb/text()', $entry);
$item['verb'] = XML::getFirstNodeValue($xpath, 'activity:verb/text()', $entry);
// Delete a message
if (in_array($item["verb"], ['qvitter-delete-notice', Activity::DELETE, 'delete'])) {
if (in_array($item['verb'], ['qvitter-delete-notice', Activity::DELETE, 'delete'])) {
self::deleteNotice($item);
continue;
}
if (in_array($item["verb"], [Activity::O_UNFAVOURITE, Activity::UNFAVORITE])) {
if (in_array($item['verb'], [Activity::O_UNFAVOURITE, Activity::UNFAVORITE])) {
// Ignore "Unfavorite" message
Logger::info("Ignore unfavorite message ", ['item' => $item]);
Logger::info('Ignore unfavorite message ', ['item' => $item]);
continue;
}
// Deletions come with the same uri, so we check for duplicates after processing deletions
if (Post::exists(['uid' => $importer["uid"], 'uri' => $item["uri"]])) {
Logger::info('Post with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.');
if (Post::exists(['uid' => $importer['uid'], 'uri' => $item['uri']])) {
Logger::info('Post with URI ' . $item['uri'] . ' already existed for user ' . $importer['uid'] . '.');
continue;
} else {
Logger::info('Processing post with URI '.$item["uri"].' for user '.$importer["uid"].'.');
Logger::info('Processing post with URI ' . $item['uri'] . ' for user ' . $importer['uid'] . '.');
}
if ($item["verb"] == Activity::JOIN) {
if ($item['verb'] == Activity::JOIN) {
// ignore "Join" messages
Logger::info("Ignore join message ", ['item' => $item]);
Logger::info('Ignore join message ', ['item' => $item]);
continue;
}
if ($item["verb"] == "http://mastodon.social/schema/1.0/block") {
if ($item['verb'] == 'http://mastodon.social/schema/1.0/block') {
// ignore mastodon "block" messages
Logger::info("Ignore block message ", ['item' => $item]);
Logger::info('Ignore block message ', ['item' => $item]);
continue;
}
if ($item["verb"] == Activity::FOLLOW) {
if ($item['verb'] == Activity::FOLLOW) {
Contact::addRelationship($importer, $contact, $item);
continue;
}
if ($item["verb"] == Activity::O_UNFOLLOW) {
if ($item['verb'] == Activity::O_UNFOLLOW) {
$dummy = null;
Contact::removeFollower($contact);
continue;
}
if ($item["verb"] == Activity::FAVORITE) {
$orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue;
Logger::notice("Favorite", ['uri' => $orig_uri, 'item' => $item]);
if ($item['verb'] == Activity::FAVORITE) {
$orig_uri = $xpath->query('activity:object/atom:id', $entry)->item(0)->nodeValue;
Logger::notice('Favorite', ['uri' => $orig_uri, 'item' => $item]);
$item["verb"] = Activity::LIKE;
$item["thr-parent"] = $orig_uri;
$item["gravity"] = GRAVITY_ACTIVITY;
$item["object-type"] = Activity\ObjectType::NOTE;
$item['verb'] = Activity::LIKE;
$item['thr-parent'] = $orig_uri;
$item['gravity'] = GRAVITY_ACTIVITY;
$item['object-type'] = Activity\ObjectType::NOTE;
}
// http://activitystrea.ms/schema/1.0/rsvp-yes
if (!in_array($item["verb"], [Activity::POST, Activity::LIKE, Activity::SHARE])) {
Logger::info("Unhandled verb", ['verb' => $item["verb"], 'item' => $item]);
if (!in_array($item['verb'], [Activity::POST, Activity::LIKE, Activity::SHARE])) {
Logger::info('Unhandled verb', ['verb' => $item['verb'], 'item' => $item]);
}
self::processPost($xpath, $entry, $item, $importer);
@ -503,10 +521,10 @@ class OStatus
$valid = !$uid || DI::pConfig()->get($uid, 'system', 'accept_only_sharer') != Item::COMPLETION_NONE;
if ($valid) {
Logger::info("Item with uri ".self::$itemlist[0]['uri']." will be imported due to the system settings.");
Logger::info('Item with URI ' . self::$itemlist[0]['uri'] . ' will be imported due to the system settings.');
}
} else {
Logger::info("Item with uri ".self::$itemlist[0]['uri']." belongs to a contact (".self::$itemlist[0]['contact-id']."). It will be imported.");
Logger::info('Item with URI ' . self::$itemlist[0]['uri'] . ' belongs to a contact (' . self::$itemlist[0]['contact-id'] . '). It will be imported.');
}
if ($valid && DI::pConfig()->get($uid, 'system', 'accept_only_sharer') != Item::COMPLETION_LIKE) {
@ -519,7 +537,7 @@ class OStatus
}
}
if ($valid) {
Logger::info("Item with uri ".self::$itemlist[0]['uri']." will be imported since the thread contains posts or shares.");
Logger::info('Item with URI ' . self::$itemlist[0]['uri'] . ' will be imported since the thread contains posts or shares.');
}
}
} else {
@ -536,20 +554,20 @@ class OStatus
}
}
foreach (self::$itemlist as $item) {
$found = Post::exists(['uid' => $importer["uid"], 'uri' => $item["uri"]]);
$found = Post::exists(['uid' => $importer['uid'], 'uri' => $item['uri']]);
if ($found) {
Logger::notice("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already exists.");
Logger::notice('Item with URI ' . $item['uri'] . ' for user ' . $importer['uid'] . ' already exists.');
} elseif ($item['contact-id'] < 0) {
Logger::notice("Item with uri ".$item["uri"]." is from a blocked contact.");
Logger::notice('Item with URI ' . $item['uri'] . ' is from a blocked contact.');
} else {
$ret = Item::insert($item);
Logger::info("Item with uri ".$item["uri"]." for user ".$importer["uid"].' stored. Return value: '.$ret);
Logger::info('Item with URI ' . $item['uri'] . ' for user ' . $importer['uid'] . ' stored. Return value: ' . $ret);
}
}
}
self::$itemlist = [];
}
Logger::info('Processing done for post with URI '.$item["uri"].' for user '.$importer["uid"].'.');
Logger::info('Processing done for post with URI ' . $item['uri'] . ' for user '.$importer['uid'] . '.');
}
return true;
}
@ -565,13 +583,13 @@ class OStatus
{
$condition = ['uid' => $item['uid'], 'author-id' => $item['author-id'], 'uri' => $item['uri']];
if (!Post::exists($condition)) {
Logger::notice('Item from '.$item['author-link'].' with uri '.$item['uri'].' for user '.$item['uid']." wasn't found. We don't delete it.");
Logger::notice('Item from ' . $item['author-link'] . ' with uri ' . $item['uri'] . ' for user ' . $item['uid'] . " wasn't found. We don't delete it.");
return;
}
Item::markForDeletion($condition);
Logger::notice('Deleted item with uri '.$item['uri'].' for user '.$item['uid']);
Logger::notice('Deleted item with URI ' . $item['uri'] . ' for user ' . $item['uid']);
}
/**
@ -587,40 +605,40 @@ class OStatus
*/
private static function processPost(DOMXPath $xpath, $entry, array &$item, array $importer)
{
$item["body"] = HTML::toBBCode(XML::getFirstNodeValue($xpath, 'atom:content/text()', $entry));
$item["object-type"] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $entry);
if (($item["object-type"] == Activity\ObjectType::BOOKMARK) || ($item["object-type"] == Activity\ObjectType::EVENT)) {
$item["title"] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry);
$item["body"] = XML::getFirstNodeValue($xpath, 'atom:summary/text()', $entry);
} elseif ($item["object-type"] == Activity\ObjectType::QUESTION) {
$item["title"] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry);
$item['body'] = HTML::toBBCode(XML::getFirstNodeValue($xpath, 'atom:content/text()', $entry));
$item['object-type'] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $entry);
if (($item['object-type'] == Activity\ObjectType::BOOKMARK) || ($item['object-type'] == Activity\ObjectType::EVENT)) {
$item['title'] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry);
$item['body'] = XML::getFirstNodeValue($xpath, 'atom:summary/text()', $entry);
} elseif ($item['object-type'] == Activity\ObjectType::QUESTION) {
$item['title'] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry);
}
$item["created"] = XML::getFirstNodeValue($xpath, 'atom:published/text()', $entry);
$item["edited"] = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry);
$item['created'] = XML::getFirstNodeValue($xpath, 'atom:published/text()', $entry);
$item['edited'] = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry);
$item['conversation-uri'] = XML::getFirstNodeValue($xpath, 'ostatus:conversation/text()', $entry);
$conv = $xpath->query('ostatus:conversation', $entry);
if (is_object($conv->item(0))) {
foreach ($conv->item(0)->attributes as $attributes) {
if ($attributes->name == "ref") {
if ($attributes->name == 'ref') {
$item['conversation-uri'] = $attributes->textContent;
}
if ($attributes->name == "href") {
if ($attributes->name == 'href') {
$item['conversation-href'] = $attributes->textContent;
}
}
}
$related = "";
$related = '';
$inreplyto = $xpath->query('thr:in-reply-to', $entry);
if (is_object($inreplyto->item(0))) {
foreach ($inreplyto->item(0)->attributes as $attributes) {
if ($attributes->name == "ref") {
$item["thr-parent"] = $attributes->textContent;
if ($attributes->name == 'ref') {
$item['thr-parent'] = $attributes->textContent;
}
if ($attributes->name == "href") {
if ($attributes->name == 'href') {
$related = $attributes->textContent;
}
}
@ -628,7 +646,7 @@ class OStatus
$georsspoint = $xpath->query('georss:point', $entry);
if (!empty($georsspoint) && ($georsspoint->length > 0)) {
$item["coord"] = $georsspoint->item(0)->nodeValue;
$item['coord'] = $georsspoint->item(0)->nodeValue;
}
$categories = $xpath->query('atom:category', $entry);
@ -653,33 +671,33 @@ class OStatus
$add_body = $link_data['add_body'];
}
$repeat_of = "";
$repeat_of = '';
$notice_info = $xpath->query('statusnet:notice_info', $entry);
if ($notice_info && ($notice_info->length > 0)) {
foreach ($notice_info->item(0)->attributes as $attributes) {
if ($attributes->name == "source") {
$item["app"] = strip_tags($attributes->textContent);
if ($attributes->name == 'source') {
$item['app'] = strip_tags($attributes->textContent);
}
if ($attributes->name == "repeat_of") {
if ($attributes->name == 'repeat_of') {
$repeat_of = $attributes->textContent;
}
}
}
// Is it a repeated post?
if (($repeat_of != "") || ($item["verb"] == Activity::SHARE)) {
if (($repeat_of != '') || ($item['verb'] == Activity::SHARE)) {
$link_data = self::processRepeatedItem($xpath, $entry, $item, $importer);
if (!empty($link_data['add_body'])) {
$add_body .= $link_data['add_body'];
}
}
$item["body"] .= $add_body;
$item['body'] .= $add_body;
Tag::storeFromBody($item['uri-id'], $item['body']);
// Mastodon Content Warning
if (($item["verb"] == Activity::POST) && $xpath->evaluate('boolean(atom:summary)', $entry)) {
if (($item['verb'] == Activity::POST) && $xpath->evaluate('boolean(atom:summary)', $entry)) {
$clear_text = XML::getFirstNodeValue($xpath, 'atom:summary/text()', $entry);
if (!empty($clear_text)) {
$item['content-warning'] = HTML::toBBCode($clear_text);
@ -690,21 +708,21 @@ class OStatus
self::fetchSelf($self, $item);
}
if (!empty($item["conversation-href"])) {
if (!empty($item['conversation-href'])) {
self::fetchConversation($item['conversation-href'], $item['conversation-uri']);
}
if (isset($item["thr-parent"])) {
if (!Post::exists(['uid' => $importer["uid"], 'uri' => $item['thr-parent']])) {
if (isset($item['thr-parent'])) {
if (!Post::exists(['uid' => $importer['uid'], 'uri' => $item['thr-parent']])) {
if ($related != '') {
self::fetchRelated($related, $item["thr-parent"], $importer);
self::fetchRelated($related, $item['thr-parent'], $importer);
}
} else {
Logger::info('Reply with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.');
Logger::info('Reply with URI ' . $item['uri'] . ' already existed for user ' . $importer['uid'] . '.');
}
} else {
$item["thr-parent"] = $item["uri"];
$item["gravity"] = GRAVITY_PARENT;
$item['thr-parent'] = $item['uri'];
$item['gravity'] = GRAVITY_PARENT;
}
if (($item['author-link'] != '') && !empty($item['protocol'])) {
@ -722,7 +740,7 @@ class OStatus
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function fetchConversation($conversation, $conversation_uri)
private static function fetchConversation(string $conversation, string $conversation_uri)
{
// Ensure that we only store a conversation once in a process
if (isset(self::$conv_list[$conversation])) {
@ -786,7 +804,7 @@ class OStatus
* @return void
* @throws \Exception
*/
private static function storeConversation($xml, $conversation = '', $conversation_uri = '')
private static function storeConversation(string $xml, string $conversation = '', string $conversation_uri = '')
{
$doc = new DOMDocument();
@$doc->loadXML($xml);
@ -814,7 +832,7 @@ class OStatus
$inreplyto = $xpath->query('thr:in-reply-to', $entry);
if (is_object($inreplyto->item(0))) {
foreach ($inreplyto->item(0)->attributes as $attributes) {
if ($attributes->name == "ref") {
if ($attributes->name == 'ref') {
$conv_data['reply-to-uri'] = $attributes->textContent;
}
}
@ -825,10 +843,10 @@ class OStatus
$conv = $xpath->query('ostatus:conversation', $entry);
if (is_object($conv->item(0))) {
foreach ($conv->item(0)->attributes as $attributes) {
if ($attributes->name == "ref") {
if ($attributes->name == 'ref') {
$conv_data['conversation-uri'] = $attributes->textContent;
}
if ($attributes->name == "href") {
if ($attributes->name == 'href') {
$conv_data['conversation-href'] = $attributes->textContent;
}
}
@ -865,7 +883,7 @@ class OStatus
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function fetchSelf($self, array &$item)
private static function fetchSelf(string $self, array &$item)
{
$condition = ['item-uri' => $self, 'protocol' => [Conversation::PARCEL_DFRN,
Conversation::PARCEL_DIASPORA_DFRN, Conversation::PARCEL_LOCAL_DFRN,
@ -888,9 +906,9 @@ class OStatus
$doc->formatOutput = true;
$xml = $doc->saveXML();
$item["protocol"] = Conversation::PARCEL_SALMON;
$item["source"] = $xml;
$item["direction"] = Conversation::PULL;
$item['protocol'] = Conversation::PARCEL_SALMON;
$item['source'] = $xml;
$item['direction'] = Conversation::PULL;
Logger::info('Conversation '.$item['uri'].' is now fetched.');
}
@ -905,11 +923,18 @@ class OStatus
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function fetchRelated($related, $related_uri, $importer)
private static function fetchRelated(string $related, string $related_uri, array $importer)
{
$condition = ['item-uri' => $related_uri, 'protocol' => [Conversation::PARCEL_DFRN,
Conversation::PARCEL_DIASPORA_DFRN, Conversation::PARCEL_LOCAL_DFRN,
Conversation::PARCEL_DIRECT, Conversation::PARCEL_SALMON]];
$condition = [
'item-uri' => $related_uri,
'protocol' => [
Conversation::PARCEL_DFRN,
Conversation::PARCEL_DIASPORA_DFRN,
Conversation::PARCEL_LOCAL_DFRN,
Conversation::PARCEL_DIRECT,
Conversation::PARCEL_SALMON,
],
];
$conversation = DBA::selectFirst('conversation', ['source', 'protocol'], $condition);
if (DBA::isResult($conversation)) {
$stored = true;
@ -994,7 +1019,7 @@ class OStatus
$conversation = DBA::selectFirst('conversation', ['source'], $condition);
if (DBA::isResult($conversation)) {
$stored = true;
Logger::info('Got cached XML from conversation for URI '.$related_uri);
Logger::info('Got cached XML from conversation for URI ' . $related_uri);
$xml = $conversation['source'];
}
}
@ -1002,7 +1027,7 @@ class OStatus
if ($xml != '') {
self::process($xml, $importer, $contact, $hub, $stored, false, Conversation::PULL);
} else {
Logger::info("XML couldn't be fetched for URI: ".$related_uri." - href: ".$related);
Logger::info('XML could not be fetched for URI: ' . $related_uri . ' - href: ' . $related);
}
return;
}
@ -1019,7 +1044,7 @@ class OStatus
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function processRepeatedItem(DOMXPath $xpath, $entry, array &$item, array $importer)
private static function processRepeatedItem(DOMXPath $xpath, $entry, array &$item, array $importer): array
{
$activityobject = $xpath->query('activity:object', $entry)->item(0);
@ -1031,7 +1056,7 @@ class OStatus
$orig_uri = XML::getFirstNodeValue($xpath, 'atom:id/text()', $activityobject);
$links = $xpath->query("atom:link", $activityobject);
$links = $xpath->query('atom:link', $activityobject);
if ($links) {
$link_data = self::processLinks($links, $item);
}
@ -1042,22 +1067,22 @@ class OStatus
$orig_author = self::fetchAuthor($xpath, $activityobject, $importer, $dummy, false);
$item["author-name"] = $orig_author["author-name"];
$item["author-link"] = $orig_author["author-link"];
$item["author-id"] = $orig_author["author-id"];
$item['author-name'] = $orig_author['author-name'];
$item['author-link'] = $orig_author['author-link'];
$item['author-id'] = $orig_author['author-id'];
$item["body"] = HTML::toBBCode($orig_body);
$item["created"] = $orig_created;
$item["edited"] = $orig_edited;
$item['body'] = HTML::toBBCode($orig_body);
$item['created'] = $orig_created;
$item['edited'] = $orig_edited;
$item["uri"] = $orig_uri;
$item['uri'] = $orig_uri;
$item["verb"] = XML::getFirstNodeValue($xpath, 'activity:verb/text()', $activityobject);
$item['verb'] = XML::getFirstNodeValue($xpath, 'activity:verb/text()', $activityobject);
$item["object-type"] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $activityobject);
$item['object-type'] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $activityobject);
// Mastodon Content Warning
if (($item["verb"] == Activity::POST) && $xpath->evaluate('boolean(atom:summary)', $activityobject)) {
if (($item['verb'] == Activity::POST) && $xpath->evaluate('boolean(atom:summary)', $activityobject)) {
$clear_text = XML::getFirstNodeValue($xpath, 'atom:summary/text()', $activityobject);
if (!empty($clear_text)) {
$item['content-warning'] = HTML::toBBCode($clear_text);
@ -1067,8 +1092,8 @@ class OStatus
$inreplyto = $xpath->query('thr:in-reply-to', $activityobject);
if (is_object($inreplyto->item(0))) {
foreach ($inreplyto->item(0)->attributes as $attributes) {
if ($attributes->name == "ref") {
$item["thr-parent"] = $attributes->textContent;
if ($attributes->name == 'ref') {
$item['thr-parent'] = $attributes->textContent;
}
}
}
@ -1081,10 +1106,9 @@ class OStatus
*
* @param object $links The xml data that contain links
* @param array $item The item array
*
* @return array with data from the links
*/
private static function processLinks($links, array &$item)
private static function processLinks($links, array &$item): array
{
$link_data = ['add_body' => '', 'self' => ''];
@ -1093,24 +1117,26 @@ class OStatus
if (!empty($attribute['rel']) && !empty($attribute['href'])) {
switch ($attribute['rel']) {
case "alternate":
$item["plink"] = $attribute['href'];
if (($item["object-type"] == Activity\ObjectType::QUESTION)
|| ($item["object-type"] == Activity\ObjectType::EVENT)
case 'alternate':
$item['plink'] = $attribute['href'];
if (($item['object-type'] == Activity\ObjectType::QUESTION)
|| ($item['object-type'] == Activity\ObjectType::EVENT)
) {
Post\Media::insert(['uri-id' => $item['uri-id'], 'type' => Post\Media::UNKNOWN,
'url' => $attribute['href'], 'mimetype' => $attribute['type'] ?? null,
'size' => $attribute['length'] ?? null, 'description' => $attribute['title'] ?? null]);
}
break;
case "ostatus:conversation":
case 'ostatus:conversation':
$link_data['conversation'] = $attribute['href'];
$item['conversation-href'] = $link_data['conversation'];
if (!isset($item['conversation-uri'])) {
$item['conversation-uri'] = $item['conversation-href'];
}
break;
case "enclosure":
case 'enclosure':
$filetype = strtolower(substr($attribute['type'], 0, strpos($attribute['type'], '/')));
if ($filetype == 'image') {
$link_data['add_body'] .= "\n[img]".$attribute['href'].'[/img]';
@ -1120,10 +1146,11 @@ class OStatus
'size' => $attribute['length'] ?? null, 'description' => $attribute['title'] ?? null]);
}
break;
case "related":
if ($item["object-type"] != Activity\ObjectType::BOOKMARK) {
if (!isset($item["thr-parent"])) {
$item["thr-parent"] = $attribute['href'];
case 'related':
if ($item['object-type'] != Activity\ObjectType::BOOKMARK) {
if (!isset($item['thr-parent'])) {
$item['thr-parent'] = $attribute['href'];
}
$link_data['related'] = $attribute['href'];
} else {
@ -1132,12 +1159,16 @@ class OStatus
'size' => $attribute['length'] ?? null, 'description' => $attribute['title'] ?? null]);
}
break;
case "self":
if (empty($item["plink"])) {
$item["plink"] = $attribute['href'];
case 'self':
if (empty($item['plink'])) {
$item['plink'] = $attribute['href'];
}
$link_data['self'] = $attribute['href'];
break;
default:
Logger::warning('Unsupported rel=' . $attribute['rel'] . ', href=' . $attribute['href'] . ', object-type=' . $attribute['object-type']);
}
}
}
@ -1148,31 +1179,31 @@ class OStatus
* Create an url out of an uri
*
* @param string $href URI in the format "parameter1:parameter1:..."
*
* @return string URL in the format http(s)://....
*/
private static function convertHref($href)
private static function convertHref(string $href): string
{
$elements = explode(":", $href);
$elements = explode(':', $href);
if ((count($elements) <= 2) || ($elements[0] != "tag")) {
if ((count($elements) <= 2) || ($elements[0] != 'tag')) {
return $href;
}
$server = explode(",", $elements[1]);
$conversation = explode("=", $elements[2]);
$server = explode(',', $elements[1]);
$conversation = explode('=', $elements[2]);
if ((count($elements) == 4) && ($elements[2] == "post")) {
return "http://".$server[0]."/notice/".$elements[3];
if ((count($elements) == 4) && ($elements[2] == 'post')) {
return 'http://' . $server[0] . '/notice/' . $elements[3];
}
if ((count($conversation) != 2) || ($conversation[1] =="")) {
if ((count($conversation) != 2) || ($conversation[1] == '')) {
return $href;
}
if ($elements[3] == "objectType=thread") {
return "http://".$server[0]."/conversation/".$conversation[1];
if ($elements[3] == 'objectType=thread') {
return 'http://' . $server[0] . '/conversation/' . $conversation[1];
} else {
return "http://".$server[0]."/notice/".$conversation[1];
return 'http://' . $server[0] . '/notice/' . $conversation[1];
}
}
@ -1180,35 +1211,35 @@ class OStatus
* Cleans the body of a post if it contains picture links
*
* @param string $body The body
*
* @param integer $uriid URI id
* @return string The cleaned body
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function formatPicturePost($body, $uriid)
public static function formatPicturePost(string $body, int $uriid): string
{
$siteinfo = BBCode::getAttachedData($body);
if (($siteinfo["type"] == "photo") && (!empty($siteinfo["preview"]) || !empty($siteinfo["image"]))) {
if (isset($siteinfo["preview"])) {
$preview = $siteinfo["preview"];
if (($siteinfo['type'] == 'photo') && (!empty($siteinfo['preview']) || !empty($siteinfo['image']))) {
if (isset($siteinfo['preview'])) {
$preview = $siteinfo['preview'];
} else {
$preview = $siteinfo["image"];
$preview = $siteinfo['image'];
}
// Is it a remote picture? Then make a smaller preview here
$preview = Post\Link::getByLink($uriid, $preview, Proxy::SIZE_SMALL);
// Is it a local picture? Then make it smaller here
$preview = str_replace(["-0.jpg", "-0.png"], ["-2.jpg", "-2.png"], $preview);
$preview = str_replace(["-1.jpg", "-1.png"], ["-2.jpg", "-2.png"], $preview);
$preview = str_replace(['-0.jpg', '-0.png'], ['-2.jpg', '-2.png'], $preview);
$preview = str_replace(['-1.jpg', '-1.png'], ['-2.jpg', '-2.png'], $preview);
if (isset($siteinfo["url"])) {
$url = $siteinfo["url"];
if (isset($siteinfo['url'])) {
$url = $siteinfo['url'];
} else {
$url = $siteinfo["image"];
$url = $siteinfo['image'];
}
$body = trim($siteinfo["text"])." [url]".$url."[/url]\n[img]".$preview."[/img]";
$body = trim($siteinfo['text']) . ' [url]' . $url . "[/url]\n[img]" . $preview . '[/img]';
}
return $body;
@ -1221,54 +1252,63 @@ class OStatus
* @param array $owner Contact data of the poster
* @param string $filter The related feed filter (activity, posts or comments)
*
* @return object header root element
* @return DOMElement Header root element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function addHeader(DOMDocument $doc, array $owner, $filter)
private static function addHeader(DOMDocument $doc, array $owner, string $filter): DOMElement
{
$root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed');
$doc->appendChild($root);
$root->setAttribute("xmlns:thr", ActivityNamespace::THREAD);
$root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS);
$root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY);
$root->setAttribute("xmlns:media", ActivityNamespace::MEDIA);
$root->setAttribute("xmlns:poco", ActivityNamespace::POCO);
$root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS);
$root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET);
$root->setAttribute("xmlns:mastodon", ActivityNamespace::MASTODON);
$root->setAttribute('xmlns:thr', ActivityNamespace::THREAD);
$root->setAttribute('xmlns:georss', ActivityNamespace::GEORSS);
$root->setAttribute('xmlns:activity', ActivityNamespace::ACTIVITY);
$root->setAttribute('xmlns:media', ActivityNamespace::MEDIA);
$root->setAttribute('xmlns:poco', ActivityNamespace::POCO);
$root->setAttribute('xmlns:ostatus', ActivityNamespace::OSTATUS);
$root->setAttribute('xmlns:statusnet', ActivityNamespace::STATUSNET);
$root->setAttribute('xmlns:mastodon', ActivityNamespace::MASTODON);
$title = '';
$selfUri = '/feed/' . $owner["nick"] . '/';
$selfUri = '/feed/' . $owner['nick'] . '/';
switch ($filter) {
case 'activity':
$title = DI::l10n()->t('%s\'s timeline', $owner['name']);
$selfUri .= $filter;
break;
case 'posts':
$title = DI::l10n()->t('%s\'s posts', $owner['name']);
break;
case 'comments':
$title = DI::l10n()->t('%s\'s comments', $owner['name']);
$selfUri .= $filter;
break;
}
$selfUri = "/dfrn_poll/" . $owner["nick"];
$selfUri = '/dfrn_poll/' . $owner['nick'];
$attributes = ["uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION . "-" . DB_UPDATE_VERSION];
XML::addElement($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes);
XML::addElement($doc, $root, "id", DI::baseUrl() . "/profile/" . $owner["nick"]);
XML::addElement($doc, $root, "title", $title);
XML::addElement($doc, $root, "subtitle", sprintf("Updates from %s on %s", $owner["name"], DI::config()->get('config', 'sitename')));
XML::addElement($doc, $root, "logo", User::getAvatarUrl($owner, Proxy::SIZE_SMALL));
XML::addElement($doc, $root, "updated", DateTimeFormat::utcNow(DateTimeFormat::ATOM));
$attributes = [
'uri' => 'https://friendi.ca',
'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
];
XML::addElement($doc, $root, 'generator', FRIENDICA_PLATFORM, $attributes);
XML::addElement($doc, $root, 'id', DI::baseUrl() . '/profile/' . $owner['nick']);
XML::addElement($doc, $root, 'title', $title);
XML::addElement($doc, $root, 'subtitle', sprintf("Updates from %s on %s", $owner['name'], DI::config()->get('config', 'sitename')));
XML::addElement($doc, $root, 'logo', User::getAvatarUrl($owner, Proxy::SIZE_SMALL));
XML::addElement($doc, $root, 'updated', DateTimeFormat::utcNow(DateTimeFormat::ATOM));
$author = self::addAuthor($doc, $owner, true);
$root->appendChild($author);
$attributes = ["href" => $owner["url"], "rel" => "alternate", "type" => "text/html"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = [
'href' => $owner['url'],
'rel' => 'alternate',
'type' => 'text/html',
];
XML::addElement($doc, $root, 'link', '', $attributes);
/// @TODO We have to find out what this is
/// $attributes = array("href" => DI::baseUrl()."/sup",
@ -1276,25 +1316,30 @@ class OStatus
/// "type" => "application/json");
/// XML::addElement($doc, $root, "link", "", $attributes);
self::hublinks($doc, $root, $owner["nick"]);
self::addHubLink($doc, $root, $owner['nick']);
$attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "salmon"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['href' => DI::baseUrl() . '/salmon/' . $owner['nick'], 'rel' => 'salmon'];
XML::addElement($doc, $root, 'link', '', $attributes);
$attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-replies"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['href' => DI::baseUrl() . '/salmon/' . $owner['nick'], 'rel' => 'http://salmon-protocol.org/ns/salmon-replies'];
XML::addElement($doc, $root, 'link', '', $attributes);
$attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-mention"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['href' => DI::baseUrl() . '/salmon/' . $owner['nick'], 'rel' => 'http://salmon-protocol.org/ns/salmon-mention'];
XML::addElement($doc, $root, 'link', '', $attributes);
$attributes = ["href" => DI::baseUrl() . $selfUri, "rel" => "self", "type" => "application/atom+xml"];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = ['href' => DI::baseUrl() . $selfUri, 'rel' => 'self', 'type' => 'application/atom+xml'];
XML::addElement($doc, $root, 'link', '', $attributes);
if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
$condition = ['uid' => $owner['uid'], 'self' => false, 'pending' => false,
'archive' => false, 'hidden' => false, 'blocked' => false];
$members = DBA::count('contact', $condition);
XML::addElement($doc, $root, "statusnet:group_info", "", ["member_count" => $members]);
$members = DBA::count('contact', [
'uid' => $owner['uid'],
'self' => false,
'pending' => false,
'archive' => false,
'hidden' => false,
'blocked' => false,
]);
XML::addElement($doc, $root, 'statusnet:group_info', '', ['member_count' => $members]);
}
return $root;
@ -1304,65 +1349,70 @@ class OStatus
* Add the link to the push hubs to the XML document
*
* @param DOMDocument $doc XML document
* @param object $root XML root element where the hub links are added
* @param object $nick nick
* @param DOMElement $root XML root element where the hub links are added
* @param string $nick Nickname
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function hublinks(DOMDocument $doc, $root, $nick)
public static function addHubLink(DOMDocument $doc, DOMElement $root, string $nick)
{
$h = DI::baseUrl() . '/pubsubhubbub/'.$nick;
XML::addElement($doc, $root, "link", "", ["href" => $h, "rel" => "hub"]);
$h = DI::baseUrl() . '/pubsubhubbub/' . $nick;
XML::addElement($doc, $root, 'link', '', ['href' => $h, 'rel' => 'hub']);
}
/**
* Adds attachment data to the XML document
*
* @param DOMDocument $doc XML document
* @param object $root XML root element where the hub links are added
* @param DOMElement $root XML root element where the hub links are added
* @param array $item Data of the item that is to be posted
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getAttachment(DOMDocument $doc, $root, $item)
public static function getAttachment(DOMDocument $doc, DOMElement $root, array $item)
{
$siteinfo = BBCode::getAttachedData($item["body"]);
$siteinfo = BBCode::getAttachedData($item['body']);
switch ($siteinfo["type"]) {
switch ($siteinfo['type']) {
case 'photo':
if (!empty($siteinfo["image"])) {
$imgdata = Images::getInfoFromURLCached($siteinfo["image"]);
if (!empty($siteinfo['image'])) {
$imgdata = Images::getInfoFromURLCached($siteinfo['image']);
if ($imgdata) {
$attributes = ["rel" => "enclosure",
"href" => $siteinfo["image"],
"type" => $imgdata["mime"],
"length" => intval($imgdata["size"])];
XML::addElement($doc, $root, "link", "", $attributes);
$attributes = [
'rel' => 'enclosure',
'href' => $siteinfo['image'],
'type' => $imgdata['mime'],
'length' => intval($imgdata['size']),
];
XML::addElement($doc, $root, 'link', '', $attributes);
}
}
break;
case 'video':
$attributes = ["rel" => "enclosure",
"href" => $siteinfo["url"],
"type" => "text/html; charset=UTF-8",
"length" => "0",
"title" => ($siteinfo["title"] ?? '') ?: $siteinfo["url"],
$attributes = [
'rel' => 'enclosure',
'href' => $siteinfo['url'],
'type' => 'text/html; charset=UTF-8',
'length' => '0',
'title' => ($siteinfo['title'] ?? '') ?: $siteinfo['url'],
];
XML::addElement($doc, $root, "link", "", $attributes);
XML::addElement($doc, $root, 'link', '', $attributes);
break;
default:
break;
}
if (!DI::config()->get('system', 'ostatus_not_attach_preview') && ($siteinfo["type"] != "photo") && isset($siteinfo["image"])) {
$imgdata = Images::getInfoFromURLCached($siteinfo["image"]);
if (!DI::config()->get('system', 'ostatus_not_attach_preview') && ($siteinfo['type'] != 'photo') && isset($siteinfo['image'])) {
$imgdata = Images::getInfoFromURLCached($siteinfo['image']);
if ($imgdata) {
$attributes = ["rel" => "enclosure",
"href" => $siteinfo["image"],
"type" => $imgdata["mime"],
"length" => intval($imgdata["size"])];
$attributes = [
'rel' => 'enclosure',
'href' => $siteinfo['image'],
'type' => $imgdata['mime'],
'length' => intval($imgdata['size']),
];
XML::addElement($doc, $root, "link", "", $attributes);
XML::addElement($doc, $root, 'link', '', $attributes);
}
}
@ -1388,75 +1438,80 @@ class OStatus
* @param DOMDocument $doc XML document
* @param array $owner Contact data of the poster
* @param bool $show_profile Whether to show profile
*
* @return \DOMElement author element
* @return DOMElement Author element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function addAuthor(DOMDocument $doc, array $owner, $show_profile = true)
private static function addAuthor(DOMDocument $doc, array $owner, bool $show_profile = true): DOMElement
{
$profile = DBA::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid']]);
$author = $doc->createElement("author");
XML::addElement($doc, $author, "id", $owner["url"]);
$author = $doc->createElement('author');
XML::addElement($doc, $author, 'id', $owner['url']);
if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::GROUP);
XML::addElement($doc, $author, 'activity:object-type', Activity\ObjectType::GROUP);
} else {
XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::PERSON);
XML::addElement($doc, $author, 'activity:object-type', Activity\ObjectType::PERSON);
}
XML::addElement($doc, $author, "uri", $owner["url"]);
XML::addElement($doc, $author, "name", $owner["nick"]);
XML::addElement($doc, $author, "email", $owner["addr"]);
XML::addElement($doc, $author, 'uri', $owner['url']);
XML::addElement($doc, $author, 'name', $owner['nick']);
XML::addElement($doc, $author, 'email', $owner['addr']);
if ($show_profile) {
XML::addElement($doc, $author, "summary", BBCode::convertForUriId($owner['uri-id'], $owner["about"], BBCode::OSTATUS));
XML::addElement($doc, $author, 'summary', BBCode::convertForUriId($owner['uri-id'], $owner['about'], BBCode::OSTATUS));
}
$attributes = ["rel" => "alternate", "type" => "text/html", "href" => $owner["url"]];
XML::addElement($doc, $author, "link", "", $attributes);
$attributes = [
"rel" => "avatar",
"type" => "image/jpeg", // To-Do?
"media:width" => Proxy::PIXEL_SMALL,
"media:height" => Proxy::PIXEL_SMALL,
"href" => User::getAvatarUrl($owner, Proxy::SIZE_SMALL)];
XML::addElement($doc, $author, "link", "", $attributes);
'rel' => 'alternate',
'type' => 'text/html',
'href' => $owner['url'],
];
XML::addElement($doc, $author, 'link', '', $attributes);
if (isset($owner["thumb"])) {
$attributes = [
'rel' => 'avatar',
'type' => 'image/jpeg', // To-Do?
'media:width' => Proxy::PIXEL_SMALL,
'media:height' => Proxy::PIXEL_SMALL,
'href' => User::getAvatarUrl($owner, Proxy::SIZE_SMALL),
];
XML::addElement($doc, $author, 'link', '', $attributes);
if (isset($owner['thumb'])) {
$attributes = [
"rel" => "avatar",
"type" => "image/jpeg", // To-Do?
"media:width" => Proxy::PIXEL_THUMB,
"media:height" => Proxy::PIXEL_THUMB,
"href" => User::getAvatarUrl($owner, Proxy::SIZE_THUMB)];
XML::addElement($doc, $author, "link", "", $attributes);
'rel' => 'avatar',
'type' => 'image/jpeg', // To-Do?
'media:width' => Proxy::PIXEL_THUMB,
'media:height' => Proxy::PIXEL_THUMB,
'href' => User::getAvatarUrl($owner, Proxy::SIZE_THUMB),
];
XML::addElement($doc, $author, 'link', '', $attributes);
}
XML::addElement($doc, $author, "poco:preferredUsername", $owner["nick"]);
XML::addElement($doc, $author, "poco:displayName", $owner["name"]);
XML::addElement($doc, $author, 'poco:preferredUsername', $owner['nick']);
XML::addElement($doc, $author, 'poco:displayName', $owner['name']);
if ($show_profile) {
XML::addElement($doc, $author, "poco:note", BBCode::convertForUriId($owner['uri-id'], $owner["about"], BBCode::OSTATUS));
XML::addElement($doc, $author, 'poco:note', BBCode::convertForUriId($owner['uri-id'], $owner['about'], BBCode::OSTATUS));
if (trim($owner["location"]) != "") {
$element = $doc->createElement("poco:address");
XML::addElement($doc, $element, "poco:formatted", $owner["location"]);
if (trim($owner['location']) != '') {
$element = $doc->createElement('poco:address');
XML::addElement($doc, $element, 'poco:formatted', $owner['location']);
$author->appendChild($element);
}
}
if (DBA::isResult($profile) && !$show_profile) {
if (trim($profile["homepage"]) != "") {
$urls = $doc->createElement("poco:urls");
XML::addElement($doc, $urls, "poco:type", "homepage");
XML::addElement($doc, $urls, "poco:value", $profile["homepage"]);
XML::addElement($doc, $urls, "poco:primary", "true");
if (trim($profile['homepage']) != '') {
$urls = $doc->createElement('poco:urls');
XML::addElement($doc, $urls, 'poco:type', 'homepage');
XML::addElement($doc, $urls, 'poco:value', $profile['homepage']);
XML::addElement($doc, $urls, 'poco:primary', 'true');
$author->appendChild($urls);
}
XML::addElement($doc, $author, "followers", "", ["url" => DI::baseUrl() . "/profile/" . $owner["nick"] . "/contacts/followers"]);
XML::addElement($doc, $author, "statusnet:profile_info", "", ["local_id" => $owner["uid"]]);
XML::addElement($doc, $author, 'followers', '', ['url' => DI::baseUrl() . '/profile/' . $owner['nick'] . '/contacts/followers']);
XML::addElement($doc, $author, 'statusnet:profile_info', '', ['local_id' => $owner['uid']]);
if ($profile["publish"]) {
XML::addElement($doc, $author, "mastodon:scope", "public");
if ($profile['publish']) {
XML::addElement($doc, $author, 'mastodon:scope', 'public');
}
}
@ -1473,10 +1528,9 @@ class OStatus
* Returns the given activity if present - otherwise returns the "post" activity
*
* @param array $item Data of the item that is to be posted
*
* @return string activity
*/
public static function constructVerb(array $item)
public static function constructVerb(array $item): string
{
if (!empty($item['verb'])) {
return $item['verb'];
@ -1489,10 +1543,9 @@ class OStatus
* Returns the given object type if present - otherwise returns the "note" object type
*
* @param array $item Data of the item that is to be posted
*
* @return string Object type
*/
private static function constructObjecttype(array $item)
private static function constructObjecttype(array $item): string
{
if (!empty($item['object-type']) && in_array($item['object-type'], [Activity\ObjectType::NOTE, Activity\ObjectType::COMMENT])) {
return $item['object-type'];
@ -1509,15 +1562,15 @@ class OStatus
* @param array $owner Contact data of the poster
* @param bool $toplevel optional default false
*
* @return \DOMElement Entry element
* @return DOMElement Entry element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function entry(DOMDocument $doc, array $item, array $owner, $toplevel = false)
private static function entry(DOMDocument $doc, array $item, array $owner, bool $toplevel = false): DOMElement
{
if ($item["verb"] == Activity::LIKE) {
if ($item['verb'] == Activity::LIKE) {
return self::likeEntry($doc, $item, $owner, $toplevel);
} elseif (in_array($item["verb"], [Activity::FOLLOW, Activity::O_UNFOLLOW])) {
} elseif (in_array($item['verb'], [Activity::FOLLOW, Activity::O_UNFOLLOW])) {
return self::followEntry($doc, $item, $owner, $toplevel);
} else {
return self::noteEntry($doc, $item, $owner, $toplevel);
@ -1531,29 +1584,28 @@ class OStatus
* @param array $item Data of the item that is to be posted
* @param array $owner Contact data of the poster
* @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
*
* @return \DOMElement Entry element with "like"
* @return DOMElement Entry element with "like"
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function likeEntry(DOMDocument $doc, array $item, array $owner, $toplevel)
private static function likeEntry(DOMDocument $doc, array $item, array $owner, bool $toplevel): DOMElement
{
if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) {
Logger::info("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.");
if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
Logger::info('OStatus entry is from author ' . $owner['url'] . ' - not from ' . $item['author-link'] . '. Quitting.');
}
$entry = self::entryHeader($doc, $owner, $item, $toplevel);
$verb = ActivityNamespace::ACTIVITY_SCHEMA . "favorite";
self::entryContent($doc, $entry, $item, $owner, "Favorite", $verb, false);
$verb = ActivityNamespace::ACTIVITY_SCHEMA . 'favorite';
self::entryContent($doc, $entry, $item, $owner, 'Favorite', $verb, false);
$parent = Post::selectFirst([], ['uri' => $item["thr-parent"], 'uid' => $item["uid"]]);
$parent = Post::selectFirst([], ['uri' => $item['thr-parent'], 'uid' => $item['uid']]);
if (DBA::isResult($parent)) {
$as_object = $doc->createElement("activity:object");
$as_object = $doc->createElement('activity:object');
XML::addElement($doc, $as_object, "activity:object-type", self::constructObjecttype($parent));
XML::addElement($doc, $as_object, 'activity:object-type', self::constructObjecttype($parent));
self::entryContent($doc, $as_object, $parent, $owner, "New entry");
self::entryContent($doc, $as_object, $parent, $owner, 'New entry');
$entry->appendChild($as_object);
}
@ -1569,39 +1621,42 @@ class OStatus
* @param DOMDocument $doc XML document
* @param array $owner Contact data of the poster
* @param array $contact Contact data of the target
*
* @return object author element
* @return DOMElement author element
*/
private static function addPersonObject(DOMDocument $doc, array $owner, array $contact)
private static function addPersonObject(DOMDocument $doc, array $owner, array $contact): DOMElement
{
$object = $doc->createElement("activity:object");
XML::addElement($doc, $object, "activity:object-type", Activity\ObjectType::PERSON);
$object = $doc->createElement('activity:object');
XML::addElement($doc, $object, 'activity:object-type', Activity\ObjectType::PERSON);
if ($contact['network'] == Protocol::PHANTOM) {
XML::addElement($doc, $object, "id", $contact['url']);
XML::addElement($doc, $object, 'id', $contact['url']);
return $object;
}
XML::addElement($doc, $object, "id", $contact["alias"]);
XML::addElement($doc, $object, "title", $contact["nick"]);
XML::addElement($doc, $object, 'id', $contact['alias']);
XML::addElement($doc, $object, 'title', $contact['nick']);
$attributes = ["rel" => "alternate", "type" => "text/html", "href" => $contact["url"]];
XML::addElement($doc, $object, "link", "", $attributes);
XML::addElement($doc, $object, 'link', '', [
'rel' => 'alternate',
'type' => 'text/html',
'href' => $contact['url'],
]);
$attributes = [
"rel" => "avatar",
"type" => "image/jpeg", // To-Do?
"media:width" => 300,
"media:height" => 300,
"href" => $contact["photo"]];
XML::addElement($doc, $object, "link", "", $attributes);
'rel' => 'avatar',
'type' => 'image/jpeg', // To-Do?
'media:width' => 300,
'media:height' => 300,
'href' => $contact['photo'],
];
XML::addElement($doc, $object, 'link', '', $attributes);
XML::addElement($doc, $object, "poco:preferredUsername", $contact["nick"]);
XML::addElement($doc, $object, "poco:displayName", $contact["name"]);
XML::addElement($doc, $object, 'poco:preferredUsername', $contact['nick']);
XML::addElement($doc, $object, 'poco:displayName', $contact['name']);
if (trim($contact["location"]) != "") {
$element = $doc->createElement("poco:address");
XML::addElement($doc, $element, "poco:formatted", $contact["location"]);
if (trim($contact['location']) != '') {
$element = $doc->createElement('poco:address');
XML::addElement($doc, $element, 'poco:formatted', $contact['location']);
$object->appendChild($element);
}
@ -1615,16 +1670,15 @@ class OStatus
* @param array $item Data of the follow/unfollow message
* @param array $owner Contact data of the poster
* @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
*
* @return \DOMElement Entry element
* @return DOMElement Entry element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function followEntry(DOMDocument $doc, array $item, array $owner, $toplevel)
private static function followEntry(DOMDocument $doc, array $item, array $owner, bool $toplevel): DOMElement
{
$item["id"] = $item['parent'] = 0;
$item["created"] = $item["edited"] = date("c");
$item["private"] = Item::PRIVATE;
$item['id'] = $item['parent'] = 0;
$item['created'] = $item['edited'] = date('c');
$item['private'] = Item::PRIVATE;
$contact = Contact::getByURL($item['follow']);
$item['follow'] = $contact['url'];
@ -1635,7 +1689,7 @@ class OStatus
$contact['alias'] = $contact['url'];
}
$condition = ['uid' => $owner['uid'], 'nurl' => Strings::normaliseLink($contact["url"])];
$condition = ['uid' => $owner['uid'], 'nurl' => Strings::normaliseLink($contact['url'])];
$user_contact = DBA::selectFirst('contact', ['id'], $condition);
if (DBA::isResult($user_contact)) {
@ -1647,19 +1701,19 @@ class OStatus
if ($item['verb'] == Activity::FOLLOW) {
$message = DI::l10n()->t('%s is now following %s.');
$title = DI::l10n()->t('following');
$action = "subscription";
$action = 'subscription';
} else {
$message = DI::l10n()->t('%s stopped following %s.');
$title = DI::l10n()->t('stopped following');
$action = "unfollow";
$action = 'unfollow';
}
$item["uri"] = $item['parent-uri'] = $item['thr-parent']
$item['uri'] = $item['parent-uri'] = $item['thr-parent']
= 'tag:' . DI::baseUrl()->getHostname().
','.date('Y-m-d').':'.$action.':'.$owner['uid'].
':person:'.$connect_id.':'.$item['created'];
$item["body"] = sprintf($message, $owner["nick"], $contact["nick"]);
$item['body'] = sprintf($message, $owner['nick'], $contact['nick']);
$entry = self::entryHeader($doc, $owner, $item, $toplevel);
@ -1680,30 +1734,29 @@ class OStatus
* @param array $item Data of the item that is to be posted
* @param array $owner Contact data of the poster
* @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
*
* @return \DOMElement Entry element
* @return DOMElement Entry element
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function noteEntry(DOMDocument $doc, array $item, array $owner, $toplevel)
private static function noteEntry(DOMDocument $doc, array $item, array $owner, bool $toplevel): DOMElement
{
if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) {
Logger::info("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.");
if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
Logger::info('OStatus entry is from author ' . $owner['url'] . ' - not from ' . $item['author-link'] . '. Quitting.');
}
if (!$toplevel) {
if (!empty($item['title'])) {
$title = BBCode::convertForUriId($item['uri-id'], $item['title'], BBCode::OSTATUS);
} else {
$title = sprintf("New note by %s", $owner["nick"]);
$title = sprintf('New note by %s', $owner['nick']);
}
} else {
$title = sprintf("New comment by %s", $owner["nick"]);
$title = sprintf('New comment by %s', $owner['nick']);
}
$entry = self::entryHeader($doc, $owner, $item, $toplevel);
XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::NOTE);
XML::addElement($doc, $entry, 'activity:object-type', Activity\ObjectType::NOTE);
self::entryContent($doc, $entry, $item, $owner, $title, '', true);
@ -1719,15 +1772,14 @@ class OStatus
* @param array $owner Contact data of the poster
* @param array $item
* @param bool $toplevel Is it for en entry element (false) or a feed entry (true)?
*
* @return \DOMElement The entry element where the elements are added
* @return DOMElement The entry element where the elements are added
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function entryHeader(DOMDocument $doc, array $owner, array $item, $toplevel)
public static function entryHeader(DOMDocument $doc, array $owner, array $item, bool $toplevel): DOMElement
{
if (!$toplevel) {
$entry = $doc->createElement("entry");
$entry = $doc->createElement('entry');
if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
$contact = Contact::getByURL($item['author-link']) ?: $owner;
@ -1736,16 +1788,16 @@ class OStatus
$entry->appendChild($author);
}
} else {
$entry = $doc->createElementNS(ActivityNamespace::ATOM1, "entry");
$entry = $doc->createElementNS(ActivityNamespace::ATOM1, 'entry');
$entry->setAttribute("xmlns:thr", ActivityNamespace::THREAD);
$entry->setAttribute("xmlns:georss", ActivityNamespace::GEORSS);
$entry->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY);
$entry->setAttribute("xmlns:media", ActivityNamespace::MEDIA);
$entry->setAttribute("xmlns:poco", ActivityNamespace::POCO);
$entry->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS);
$entry->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET);
$entry->setAttribute("xmlns:mastodon", ActivityNamespace::MASTODON);
$entry->setAttribute('xmlns:thr', ActivityNamespace::THREAD);
$entry->setAttribute('xmlns:georss', ActivityNamespace::GEORSS);
$entry->setAttribute('xmlns:activity', ActivityNamespace::ACTIVITY);
$entry->setAttribute('xmlns:media', ActivityNamespace::MEDIA);
$entry->setAttribute('xmlns:poco', ActivityNamespace::POCO);
$entry->setAttribute('xmlns:ostatus', ActivityNamespace::OSTATUS);
$entry->setAttribute('xmlns:statusnet', ActivityNamespace::STATUSNET);
$entry->setAttribute('xmlns:mastodon', ActivityNamespace::MASTODON);
$author = self::addAuthor($doc, $owner);
$entry->appendChild($author);
@ -1758,7 +1810,7 @@ class OStatus
* Adds elements to the XML document
*
* @param DOMDocument $doc XML document
* @param \DOMElement $entry Entry element where the content is added
* @param DOMElement $entry Entry element where the content is added
* @param array $item Data of the item that is to be posted
* @param array $owner Contact data of the poster
* @param string $title Title for the post
@ -1767,38 +1819,40 @@ class OStatus
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function entryContent(DOMDocument $doc, \DOMElement $entry, array $item, array $owner, $title, $verb = "", $complete = true)
private static function entryContent(DOMDocument $doc, DOMElement $entry, array $item, array $owner, string $title, string $verb = '', bool $complete = true)
{
if ($verb == "") {
if ($verb == '') {
$verb = self::constructVerb($item);
}
XML::addElement($doc, $entry, "id", $item["uri"]);
XML::addElement($doc, $entry, "title", html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
XML::addElement($doc, $entry, 'id', $item['uri']);
XML::addElement($doc, $entry, 'title', html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
$body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
$body = self::formatPicturePost($body, $item['uri-id']);
if (!empty($item['title'])) {
$body = "[b]".$item['title']."[/b]\n\n".$body;
$body = '[b]' . $item['title'] . "[/b]\n\n" . $body;
}
$body = BBCode::convertForUriId($item['uri-id'], $body, BBCode::OSTATUS);
XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);
XML::addElement($doc, $entry, 'content', $body, ['type' => 'html']);
XML::addElement($doc, $entry, "link", "", ["rel" => "alternate", "type" => "text/html",
"href" => DI::baseUrl()."/display/".$item["guid"]]
);
XML::addElement($doc, $entry, 'link', '', [
'rel' => 'alternate',
'type' => 'text/html',
'href' => DI::baseUrl() . '/display/' . $item['guid'],
]);
if ($complete && ($item["id"] > 0)) {
XML::addElement($doc, $entry, "status_net", "", ["notice_id" => $item["id"]]);
if ($complete && ($item['id'] > 0)) {
XML::addElement($doc, $entry, 'status_net', '', ['notice_id' => $item['id']]);
}
XML::addElement($doc, $entry, "activity:verb", $verb);
XML::addElement($doc, $entry, 'activity:verb', $verb);
XML::addElement($doc, $entry, "published", DateTimeFormat::utc($item["created"]."+00:00", DateTimeFormat::ATOM));
XML::addElement($doc, $entry, "updated", DateTimeFormat::utc($item["edited"]."+00:00", DateTimeFormat::ATOM));
XML::addElement($doc, $entry, 'published', DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM));
XML::addElement($doc, $entry, 'updated', DateTimeFormat::utc($item['edited'] . '+00:00', DateTimeFormat::ATOM));
}
/**
@ -1812,7 +1866,7 @@ class OStatus
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function entryFooter(DOMDocument $doc, $entry, array $item, array $owner, $complete = true)
private static function entryFooter(DOMDocument $doc, $entry, array $item, array $owner, bool $complete = true)
{
$mentioned = [];
@ -1861,14 +1915,15 @@ class OStatus
}
}
XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:conversation", "href" => $conversation_href]);
XML::addElement($doc, $entry, 'link', '', ['rel' => 'ostatus:conversation', 'href' => $conversation_href]);
$attributes = [
"href" => $conversation_href,
"local_id" => $item['parent'],
"ref" => $conversation_uri];
'href' => $conversation_href,
'local_id' => $item['parent'],
'ref' => $conversation_uri,
];
XML::addElement($doc, $entry, "ostatus:conversation", $conversation_uri, $attributes);
XML::addElement($doc, $entry, 'ostatus:conversation', $conversation_uri, $attributes);
}
// uri-id isn't present for follow entry pseudo-items
@ -1880,72 +1935,70 @@ class OStatus
// Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS)
$newmentions = [];
foreach ($mentioned as $mention) {
$newmentions[str_replace("http://", "https://", $mention)] = str_replace("http://", "https://", $mention);
$newmentions[str_replace("https://", "http://", $mention)] = str_replace("https://", "http://", $mention);
$newmentions[str_replace('http://', 'https://', $mention)] = str_replace('http://', 'https://', $mention);
$newmentions[str_replace('https://', 'http://', $mention)] = str_replace('https://', 'http://', $mention);
}
$mentioned = $newmentions;
foreach ($mentioned as $mention) {
$contact = Contact::getByURL($mention, false, ['contact-type']);
if (!empty($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) {
XML::addElement($doc, $entry, "link", "",
[
"rel" => "mentioned",
"ostatus:object-type" => Activity\ObjectType::GROUP,
"href" => $mention]
);
XML::addElement($doc, $entry, 'link', '', [
'rel' => 'mentioned',
'ostatus:object-type' => Activity\ObjectType::GROUP,
'href' => $mention,
]);
} else {
XML::addElement($doc, $entry, "link", "",
[
"rel" => "mentioned",
"ostatus:object-type" => Activity\ObjectType::PERSON,
"href" => $mention]
);
XML::addElement($doc, $entry, 'link', '', [
'rel' => 'mentioned',
'ostatus:object-type' => Activity\ObjectType::PERSON,
'href' => $mention,
]);
}
}
if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
XML::addElement($doc, $entry, "link", "", [
"rel" => "mentioned",
"ostatus:object-type" => "http://activitystrea.ms/schema/1.0/group",
"href" => $owner['url']
XML::addElement($doc, $entry, 'link', '', [
'rel' => 'mentioned',
'ostatus:object-type' => 'http://activitystrea.ms/schema/1.0/group',
'href' => $owner['url']
]);
}
if ($item['private'] != Item::PRIVATE) {
XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:attention",
"href" => "http://activityschema.org/collection/public"]);
XML::addElement($doc, $entry, "link", "", ["rel" => "mentioned",
"ostatus:object-type" => "http://activitystrea.ms/schema/1.0/collection",
"href" => "http://activityschema.org/collection/public"]);
XML::addElement($doc, $entry, "mastodon:scope", "public");
XML::addElement($doc, $entry, 'link', '', ['rel' => 'ostatus:attention',
'href' => 'http://activityschema.org/collection/public']);
XML::addElement($doc, $entry, 'link', '', ['rel' => 'mentioned',
'ostatus:object-type' => 'http://activitystrea.ms/schema/1.0/collection',
'href' => 'http://activityschema.org/collection/public']);
XML::addElement($doc, $entry, 'mastodon:scope', 'public');
}
foreach ($tags as $tag) {
if ($tag['type'] == Tag::HASHTAG) {
XML::addElement($doc, $entry, "category", "", ["term" => $tag['name']]);
XML::addElement($doc, $entry, 'category', '', ['term' => $tag['name']]);
}
}
self::getAttachment($doc, $entry, $item);
if ($complete && ($item["id"] > 0)) {
$app = $item["app"];
if ($app == "") {
$app = "web";
if ($complete && ($item['id'] > 0)) {
$app = $item['app'];
if ($app == '') {
$app = 'web';
}
$attributes = ["local_id" => $item["id"], "source" => $app];
$attributes = ['local_id' => $item['id'], 'source' => $app];
if (isset($parent["id"])) {
$attributes["repeat_of"] = $parent["id"];
if (isset($parent['id'])) {
$attributes['repeat_of'] = $parent['id'];
}
if ($item["coord"] != "") {
XML::addElement($doc, $entry, "georss:point", $item["coord"]);
if ($item['coord'] != '') {
XML::addElement($doc, $entry, 'georss:point', $item['coord']);
}
XML::addElement($doc, $entry, "statusnet:notice_info", "", $attributes);
XML::addElement($doc, $entry, 'statusnet:notice_info', '', $attributes);
}
}
@ -1965,21 +2018,20 @@ class OStatus
* @param integer $max_items Number of maximum items to fetch
* @param string $filter Feed items filter (activity, posts or comments)
* @param boolean $nocache Wether to bypass caching
*
* @return string XML feed
* @return string XML feed or empty string on error
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function feed($owner_nick, &$last_update, $max_items = 300, $filter = 'activity', $nocache = false)
public static function feed(string $owner_nick, string &$last_update, int $max_items = 300, string $filter = 'activity', bool $nocache = false): string
{
$stamp = microtime(true);
$owner = User::getOwnerDataByNick($owner_nick);
if (!$owner) {
return;
return '';
}
$cachekey = "ostatus:feed:" . $owner_nick . ":" . $filter . ":" . $last_update;
$cachekey = 'ostatus:feed:' . $owner_nick . ':' . $filter . ':' . $last_update;
$previous_created = $last_update;
@ -1998,11 +2050,16 @@ class OStatus
}
$check_date = DateTimeFormat::utc($last_update);
$authorid = Contact::getIdForURL($owner["url"]);
$authorid = Contact::getIdForURL($owner['url']);
$condition = ["`uid` = ? AND `received` > ? AND NOT `deleted`
AND `private` != ? AND `visible` AND `wall` AND `parent-network` IN (?, ?)",
$owner["uid"], $check_date, Item::PRIVATE, Protocol::OSTATUS, Protocol::DFRN];
$condition = [
"`uid` = ? AND `received` > ? AND NOT `deleted` AND `private` != ? AND `visible` AND `wall` AND `parent-network` IN (?, ?)",
$owner['uid'],
$check_date,
Item::PRIVATE,
Protocol::OSTATUS,
Protocol::DFRN,
];
if ($filter === 'comments') {
$condition[0] .= " AND `object-type` = ? ";
@ -2011,7 +2068,7 @@ class OStatus
if ($owner['contact-type'] != Contact::TYPE_COMMUNITY) {
$condition[0] .= " AND `contact-id` = ? AND `author-id` = ?";
$condition[] = $owner["id"];
$condition[] = $owner['id'];
$condition[] = $authorid;
}
@ -2035,7 +2092,7 @@ class OStatus
$item['body'] .= '🍼';
}
if (in_array($item["verb"], [Activity::FOLLOW, Activity::O_UNFOLLOW, Activity::LIKE])) {
if (in_array($item['verb'], [Activity::FOLLOW, Activity::O_UNFOLLOW, Activity::LIKE])) {
continue;
}
@ -2067,7 +2124,7 @@ class OStatus
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function salmon(array $item, array $owner)
public static function salmon(array $item, array $owner): string
{
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
@ -2091,7 +2148,7 @@ class OStatus
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function isSupportedByContactUrl($url)
public static function isSupportedByContactUrl(string $url): bool
{
$probe = Probe::uri($url, Protocol::OSTATUS);
return $probe['network'] == Protocol::OSTATUS;

View file

@ -55,7 +55,7 @@ class Relay
* @param string $url
* @return boolean "true" is the post is wanted by the system
*/
public static function isSolicitedPost(array $tags, string $body, int $authorid, string $url, string $network = '')
public static function isSolicitedPost(array $tags, string $body, int $authorid, string $url, string $network = ''): bool
{
$config = DI::config();
@ -139,6 +139,7 @@ class Relay
*
* @param array $gserver Global server record
* @param array $fields Optional network specific fields
* @return void
* @throws \Exception
*/
public static function updateContact(array $gserver, array $fields = [])
@ -198,6 +199,7 @@ class Relay
* The relay contact is a technical contact entry that exists once per server.
*
* @param array $contact of the relay contact
* @return void
*/
public static function markForArchival(array $contact)
{
@ -229,15 +231,14 @@ class Relay
* @param integer $item_id id of the item that is sent
* @param array $contacts Previously fetched contacts
* @param array $networks Networks of the relay servers
*
* @return array of relay servers
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getDirectRelayList(int $item_id)
public static function getDirectRelayList(int $item_id): array
{
$serverlist = [];
if (!DI::config()->get("system", "relay_directly", false)) {
if (!DI::config()->get('system', 'relay_directly', false)) {
return [];
}
@ -302,10 +303,10 @@ class Relay
* Return a list of relay servers
*
* @param array $fields Field list
* @return array
* @return array List of relay servers
* @throws Exception
*/
public static function getList($fields = []):array
public static function getList(array $fields = []): array
{
return DBA::selectToArray('apcontact', $fields,
["`type` = ? AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` = ?)", 'Application', 0, Contact::FRIEND]);
@ -316,7 +317,7 @@ class Relay
*
* @param array $gserver Global server record
* @param array $fields Fieldlist
* @return array with the contact
* @return array|bool Array with the contact or false on error
* @throws \Exception
*/
private static function getContact(array $gserver, array $fields = ['batch', 'id', 'url', 'name', 'network', 'protocol', 'archive', 'blocked'])
@ -344,6 +345,8 @@ class Relay
/**
* Resubscribe to all relay servers
*
* @return void
*/
public static function reSubscribe()
{

View file

@ -40,10 +40,10 @@ class Salmon
/**
* @param string $uri Uniform Resource Identifier
* @param string $keyhash encoded key
* @return mixed
* @return string Key or empty string on any errors
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getKey(string $uri, string $keyhash)
public static function getKey(string $uri, string $keyhash): string
{
$ret = [];
@ -83,13 +83,13 @@ class Salmon
Logger::notice('Key located', ['ret' => $ret]);
if (count($ret) == 1) {
// We only found one one key so we don't care if the hash matches.
// If it's the wrong key we'll find out soon enough because
// message verification will fail. This also covers some older
// software which don't supply a keyhash. As long as they only
// have one key we'll be right.
return $ret[0];
/* We only found one one key so we don't care if the hash matches.
* If it's the wrong key we'll find out soon enough because
* message verification will fail. This also covers some older
* software which don't supply a keyhash. As long as they only
* have one key we'll be right.
*/
return (string) $ret[0];
} else {
foreach ($ret as $a) {
$hash = Strings::base64UrlEncode(hash('sha256', $a));

View file

@ -34,23 +34,23 @@ class Images
/**
* Maps Mime types to Imagick formats
*
* @return array
* @return array Format map
*/
public static function getFormatsMap()
{
$m = [
return [
'image/jpeg' => 'JPG',
'image/jpg' => 'JPG',
'image/png' => 'PNG',
'image/gif' => 'GIF'
'image/gif' => 'GIF',
];
return $m;
}
/**
* Return file extension for mime type
* @param string $mimetype
* @return string
* Return file extension for MIME type
*
* @param string $mimetype MIME type
* @return string File extension for MIME type
*/
public static function getExtensionByMimeType(string $mimetype): string
{
@ -63,9 +63,14 @@ class Images
$imagetype = IMAGETYPE_GIF;
break;
default:
case 'image/jpeg':
case 'image/jpg':
$imagetype = IMAGETYPE_JPEG;
break;
default: // Unknown type must be a blob then
return 'blob';
break;
}
return image_type_to_extension($imagetype);
@ -76,11 +81,13 @@ class Images
*
* @return array
*/
public static function supportedTypes()
public static function supportedTypes(): array
{
$types = [
'image/jpeg' => 'jpg'
'image/jpeg' => 'jpg',
'image/jpg' => 'jpg',
];
if (class_exists('Imagick')) {
// Imagick::queryFormats won't help us a lot there...
// At least, not yet, other parts of friendica uses this array
@ -102,21 +109,20 @@ class Images
*
* @param string $image_data Image data
* @param string $filename File name (for guessing the type via the extension)
* @param string $mime default mime type
*
* @return string
* @param string $default Default MIME type
* @return string MIME type
* @throws \Exception
*/
public static function getMimeTypeByData(string $image_data, string $filename = '', string $mime = '')
public static function getMimeTypeByData(string $image_data, string $filename = '', string $default = ''): string
{
if (substr($mime, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mime]);
return $mime;
if (substr($default, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
return $default;
}
$image = @getimagesizefromstring($image_data);
if (!empty($image['mime'])) {
Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $mime, 'mime' => $image['mime']]);
Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $default, 'mime' => $image['mime']]);
return $image['mime'];
}
@ -128,21 +134,20 @@ class Images
*
* @param string $sourcefile Source file of the image
* @param string $filename File name (for guessing the type via the extension)
* @param string $mime default mime type
*
* @return string
* @param string $default default MIME type
* @return string MIME type
* @throws \Exception
*/
public static function getMimeTypeBySource(string $sourcefile, string $filename = '', string $mime = '')
public static function getMimeTypeBySource(string $sourcefile, string $filename = '', string $default = ''): string
{
if (substr($mime, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mime]);
return $mime;
if (substr($default, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
return $default;
}
$image = @getimagesize($sourcefile);
if (!empty($image['mime'])) {
Logger::info('Mime type detected via file', ['filename' => $filename, 'default' => $mime, 'image' => $image]);
Logger::info('Mime type detected via file', ['filename' => $filename, 'default' => $default, 'image' => $image]);
return $image['mime'];
}
@ -150,14 +155,13 @@ class Images
}
/**
* Guess image mimetype from the filename
* Guess image MIME type from the filename's extension
*
* @param string $filename Image filename
*
* @return string
* @param string $filename Image filename
* @return string Guessed MIME type by extension
* @throws \Exception
*/
public static function guessTypeByExtension(string $filename)
public static function guessTypeByExtension(string $filename): string
{
$ext = pathinfo(parse_url($filename, PHP_URL_PATH), PATHINFO_EXTENSION);
$types = self::supportedTypes();
@ -173,11 +177,13 @@ class Images
}
/**
* @param string $url
* @return array
* Gets info array from given URL, cached data has priority
*
* @param string $url URL
* @return array Info
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getInfoFromURLCached($url)
public static function getInfoFromURLCached(string $url): array
{
$data = [];
@ -195,15 +201,17 @@ class Images
DI::cache()->set($cacheKey, $data);
}
return $data;
return $data ?? [];
}
/**
* @param string $url
* @return array
* Gets info from URL uncached
*
* @param string $url URL
* @return array Info array
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getInfoFromURL($url)
public static function getInfoFromURL(string $url): array
{
$data = [];
@ -239,16 +247,18 @@ class Images
$data['size'] = $filesize;
}
return $data;
return $data ?? [];
}
/**
* @param integer $width
* @param integer $height
* @param integer $max
* @return array
* Returns scaling information
*
* @param integer $width Width
* @param integer $height Height
* @param integer $max Max width/height
* @return array Scaling dimensions
*/
public static function getScalingDimensions($width, $height, $max)
public static function getScalingDimensions(int $width, int $height, int $max): array
{
if ((!$width) || (!$height)) {
return ['width' => 0, 'height' => 0];

View file

@ -168,7 +168,7 @@ class XML
foreach ($attributes as $key => $value) {
$attribute = $doc->createAttribute($key);
$attribute->value = self::escape($value);
$attribute->value = self::escape($value ?? '');
$element->appendChild($attribute);
}
return $element;
@ -177,7 +177,7 @@ class XML
/**
* Create an XML and append it to the parent object
*
* @param DOMDocument $doc XML root
* @param DOMDocument $doc XML root
* @param object $parent parent object
* @param string $element XML element name
* @param string $value XML value

View file

@ -475,12 +475,13 @@ class Delivery
* @param array $owner Owner record of the sender
* @param array $target_item Item record of the content
* @param array $thr_parent Item record of the direct parent in the thread
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function deliverMail(string $cmd, array $contact, array $owner, array $target_item, array $thr_parent)
{
if (DI::config()->get('system','imap_disabled')) {
if (DI::config()->get('system', 'imap_disabled')) {
return;
}
@ -570,10 +571,16 @@ class Delivery
}
}
Email::send($addr, $subject, $headers, $target_item);
// Try to send email
$success = Email::send($addr, $subject, $headers, $target_item);
Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::MAIL);
Logger::info('Delivered via mail', ['guid' => $target_item['guid'], 'to' => $addr, 'subject' => $subject]);
if ($success) {
// Success
Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::MAIL);
Logger::info('Delivered via mail', ['guid' => $target_item['guid'], 'to' => $addr, 'subject' => $subject]);
} else {
// Failed
Logger::warning('Delivery of mail has FAILED', ['to' => $addr, 'subject' => $subject, 'guid' => $target_item['guid']]);
}
}
}

View file

@ -31,10 +31,9 @@ class Image
* Give all available options for the background image
*
* @param array $arr Array with the present user settings
*
* @return array Array with the immage options
*/
public static function get_options($arr)
public static function get_options(array $arr): array
{
$bg_image_options = [
'stretch' => ['frio_bg_image_option', DI::l10n()->t('Top Banner'), 'stretch', DI::l10n()->t('Resize image to the width of the screen and show background color below on long pages.'), ($arr['bg_image_option'] == 'stretch')],