X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fdfrn.php;h=14be7473056dbe173b307902ffa478bf570abf66;hb=778935eb48e9700ca2e879b1fe377541817705a0;hp=90370694ecfdea66e7a05372bcd9e1d78fad0847;hpb=3890415767aa687cf7a2e566e0f07b9c663f3de1;p=friendica.git diff --git a/include/dfrn.php b/include/dfrn.php index 90370694ec..14be747305 100644 --- a/include/dfrn.php +++ b/include/dfrn.php @@ -18,7 +18,8 @@ require_once("include/event.php"); require_once("include/text.php"); require_once("include/oembed.php"); require_once("include/html2bbcode.php"); -require_once("library/HTMLPurifier.auto.php"); +require_once("include/bbcode.php"); +require_once("include/xml.php"); /** * @brief This class contain functions to create and send DFRN XML files @@ -26,9 +27,9 @@ require_once("library/HTMLPurifier.auto.php"); */ class dfrn { - const DFRN_TOP_LEVEL = 0; - const DFRN_REPLY = 1; - const DFRN_REPLY_RC = 2; + const DFRN_TOP_LEVEL = 0; // Top level posting + const DFRN_REPLY = 1; // Regular reply that is stored locally + const DFRN_REPLY_RC = 2; // Reply that will be relayed /** * @brief Generates the atom entries for delivery.php @@ -40,7 +41,7 @@ class dfrn { * * @return string DFRN entries */ - function entries($items,$owner) { + public static function entries($items,$owner) { $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; @@ -70,7 +71,7 @@ class dfrn { * * @return string DFRN feed entries */ - function feed($dfrn_id, $owner_nick, $last_update, $direction = 0) { + public static function feed($dfrn_id, $owner_nick, $last_update, $direction = 0) { $a = get_app(); @@ -85,7 +86,7 @@ class dfrn { $converse = true; if($a->argv[$x] == 'starred') $starred = true; - if($a->argv[$x] === 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) + if($a->argv[$x] == 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) $category = $a->argv[$x+1]; } } @@ -96,7 +97,7 @@ class dfrn { $sql_extra = " AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' "; - $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags` + $r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags` FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1", dbesc($owner_nick) @@ -106,7 +107,7 @@ class dfrn { killme(); $owner = $r[0]; - $owner_id = $owner['user_uid']; + $owner_id = $owner['uid']; $owner_nick = $owner['nickname']; $sql_post_table = ""; @@ -244,7 +245,7 @@ class dfrn { foreach($items as $item) { // prevent private email from leaking. - if($item['network'] === NETWORK_MAIL) + if($item['network'] == NETWORK_MAIL) continue; // public feeds get html, our own nodes use bbcode @@ -277,7 +278,7 @@ class dfrn { * * @return string DFRN mail */ - function mail($item, $owner) { + public static function mail($item, $owner) { $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; @@ -286,17 +287,17 @@ class dfrn { $mail = $doc->createElement("dfrn:mail"); $sender = $doc->createElement("dfrn:sender"); - xml_add_element($doc, $sender, "dfrn:name", $owner['name']); - xml_add_element($doc, $sender, "dfrn:uri", $owner['url']); - xml_add_element($doc, $sender, "dfrn:avatar", $owner['thumb']); + xml::add_element($doc, $sender, "dfrn:name", $owner['name']); + xml::add_element($doc, $sender, "dfrn:uri", $owner['url']); + xml::add_element($doc, $sender, "dfrn:avatar", $owner['thumb']); $mail->appendChild($sender); - xml_add_element($doc, $mail, "dfrn:id", $item['uri']); - xml_add_element($doc, $mail, "dfrn:in-reply-to", $item['parent-uri']); - xml_add_element($doc, $mail, "dfrn:sentdate", datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME)); - xml_add_element($doc, $mail, "dfrn:subject", $item['title']); - xml_add_element($doc, $mail, "dfrn:content", $item['body']); + xml::add_element($doc, $mail, "dfrn:id", $item['uri']); + xml::add_element($doc, $mail, "dfrn:in-reply-to", $item['parent-uri']); + xml::add_element($doc, $mail, "dfrn:sentdate", datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME)); + xml::add_element($doc, $mail, "dfrn:subject", $item['title']); + xml::add_element($doc, $mail, "dfrn:content", $item['body']); $root->appendChild($mail); @@ -311,7 +312,7 @@ class dfrn { * * @return string DFRN suggestions */ - function fsuggest($item, $owner) { + public static function fsuggest($item, $owner) { $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; @@ -319,11 +320,11 @@ class dfrn { $suggest = $doc->createElement("dfrn:suggest"); - xml_add_element($doc, $suggest, "dfrn:url", $item['url']); - xml_add_element($doc, $suggest, "dfrn:name", $item['name']); - xml_add_element($doc, $suggest, "dfrn:photo", $item['photo']); - xml_add_element($doc, $suggest, "dfrn:request", $item['request']); - xml_add_element($doc, $suggest, "dfrn:note", $item['note']); + xml::add_element($doc, $suggest, "dfrn:url", $item['url']); + xml::add_element($doc, $suggest, "dfrn:name", $item['name']); + xml::add_element($doc, $suggest, "dfrn:photo", $item['photo']); + xml::add_element($doc, $suggest, "dfrn:request", $item['request']); + xml::add_element($doc, $suggest, "dfrn:note", $item['note']); $root->appendChild($suggest); @@ -338,7 +339,7 @@ class dfrn { * * @return string DFRN relocations */ - function relocate($owner, $uid) { + public static function relocate($owner, $uid) { /* get site pubkey. this could be a new installation with no site keys*/ $pubkey = get_config('system','site_pubkey'); @@ -365,16 +366,16 @@ class dfrn { $relocate = $doc->createElement("dfrn:relocate"); - xml_add_element($doc, $relocate, "dfrn:url", $owner['url']); - xml_add_element($doc, $relocate, "dfrn:name", $owner['name']); - xml_add_element($doc, $relocate, "dfrn:photo", $photos[4]); - xml_add_element($doc, $relocate, "dfrn:thumb", $photos[5]); - xml_add_element($doc, $relocate, "dfrn:micro", $photos[6]); - xml_add_element($doc, $relocate, "dfrn:request", $owner['request']); - xml_add_element($doc, $relocate, "dfrn:confirm", $owner['confirm']); - xml_add_element($doc, $relocate, "dfrn:notify", $owner['notify']); - xml_add_element($doc, $relocate, "dfrn:poll", $owner['poll']); - xml_add_element($doc, $relocate, "dfrn:sitepubkey", get_config('system','site_pubkey')); + xml::add_element($doc, $relocate, "dfrn:url", $owner['url']); + xml::add_element($doc, $relocate, "dfrn:name", $owner['name']); + xml::add_element($doc, $relocate, "dfrn:photo", $photos[4]); + xml::add_element($doc, $relocate, "dfrn:thumb", $photos[5]); + xml::add_element($doc, $relocate, "dfrn:micro", $photos[6]); + xml::add_element($doc, $relocate, "dfrn:request", $owner['request']); + xml::add_element($doc, $relocate, "dfrn:confirm", $owner['confirm']); + xml::add_element($doc, $relocate, "dfrn:notify", $owner['notify']); + xml::add_element($doc, $relocate, "dfrn:poll", $owner['poll']); + xml::add_element($doc, $relocate, "dfrn:sitepubkey", get_config('system','site_pubkey')); $root->appendChild($relocate); @@ -410,39 +411,39 @@ class dfrn { $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); - xml_add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]); - xml_add_element($doc, $root, "title", $owner["name"]); + xml::add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]); + xml::add_element($doc, $root, "title", $owner["name"]); $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION); - xml_add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); + xml::add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); $attributes = array("rel" => "license", "href" => "http://creativecommons.org/licenses/by/3.0/"); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $alternatelink); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($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); + ostatus::hublinks($doc, $root); $attributes = array("rel" => "salmon", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-replies", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-mention", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); } if ($owner['page-flags'] == PAGE_COMMUNITY) - xml_add_element($doc, $root, "dfrn:community", 1); + xml::add_element($doc, $root, "dfrn:community", 1); /// @todo We need a way to transmit the different page flags like "PAGE_PRVGROUP" - xml_add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME)); + xml::add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME)); $author = self::add_author($doc, $owner, $authorelement, $public); $root->appendChild($author); @@ -468,26 +469,26 @@ class dfrn { $picdate = datetime_convert('UTC', 'UTC', $owner['avatar-date'].'+00:00', ATOM_TIME); $attributes = array("dfrn:updated" => $namdate); - xml_add_element($doc, $author, "name", $owner["name"], $attributes); + xml::add_element($doc, $author, "name", $owner["name"], $attributes); $attributes = array("dfrn:updated" => $namdate); - xml_add_element($doc, $author, "uri", app::get_baseurl().'/profile/'.$owner["nickname"], $attributes); + xml::add_element($doc, $author, "uri", app::get_baseurl().'/profile/'.$owner["nickname"], $attributes); $attributes = array("dfrn:updated" => $namdate); - xml_add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes); + xml::add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes); $attributes = array("rel" => "photo", "type" => "image/jpeg", "dfrn:updated" => $picdate, "media:width" => 175, "media:height" => 175, "href" => $owner['photo']); - xml_add_element($doc, $author, "link", "", $attributes); + xml::add_element($doc, $author, "link", "", $attributes); $attributes = array("rel" => "avatar", "type" => "image/jpeg", "dfrn:updated" => $picdate, "media:width" => 175, "media:height" => 175, "href" => $owner['photo']); - xml_add_element($doc, $author, "link", "", $attributes); + xml::add_element($doc, $author, "link", "", $attributes); - $birthday = feed_birthday($owner['user_uid'], $owner['timezone']); + $birthday = feed_birthday($owner['uid'], $owner['timezone']); if ($birthday) - xml_add_element($doc, $author, "dfrn:birthday", $birthday); + xml::add_element($doc, $author, "dfrn:birthday", $birthday); // The following fields will only be generated if this isn't for a public feed if ($public) @@ -499,28 +500,28 @@ class dfrn { FROM `profile` INNER JOIN `user` ON `user`.`uid` = `profile`.`uid` WHERE `profile`.`is-default` AND NOT `user`.`hidewall` AND `user`.`uid` = %d", - intval($owner['user_uid'])); + intval($owner['uid'])); if ($r) { $profile = $r[0]; - xml_add_element($doc, $author, "poco:displayName", $profile["name"]); - xml_add_element($doc, $author, "poco:updated", $namdate); + xml::add_element($doc, $author, "poco:displayName", $profile["name"]); + xml::add_element($doc, $author, "poco:updated", $namdate); if (trim($profile["dob"]) != "0000-00-00") - xml_add_element($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"]))); + xml::add_element($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"]))); - xml_add_element($doc, $author, "poco:note", $profile["about"]); - xml_add_element($doc, $author, "poco:preferredUsername", $profile["nickname"]); + xml::add_element($doc, $author, "poco:note", $profile["about"]); + xml::add_element($doc, $author, "poco:preferredUsername", $profile["nickname"]); $savetz = date_default_timezone_get(); date_default_timezone_set($profile["timezone"]); - xml_add_element($doc, $author, "poco:utcOffset", date("P")); + xml::add_element($doc, $author, "poco:utcOffset", date("P")); date_default_timezone_set($savetz); if (trim($profile["homepage"]) != "") { $urls = $doc->createElement("poco:urls"); - xml_add_element($doc, $urls, "poco:type", "homepage"); - xml_add_element($doc, $urls, "poco:value", $profile["homepage"]); - xml_add_element($doc, $urls, "poco:primary", "true"); + xml::add_element($doc, $urls, "poco:type", "homepage"); + xml::add_element($doc, $urls, "poco:value", $profile["homepage"]); + xml::add_element($doc, $urls, "poco:primary", "true"); $author->appendChild($urls); } @@ -528,7 +529,7 @@ class dfrn { $keywords = explode(",", $profile["pub_keywords"]); foreach ($keywords AS $keyword) - xml_add_element($doc, $author, "poco:tags", trim($keyword)); + xml::add_element($doc, $author, "poco:tags", trim($keyword)); } @@ -536,25 +537,25 @@ class dfrn { $xmpp = ""; if (trim($xmpp) != "") { $ims = $doc->createElement("poco:ims"); - xml_add_element($doc, $ims, "poco:type", "xmpp"); - xml_add_element($doc, $ims, "poco:value", $xmpp); - xml_add_element($doc, $ims, "poco:primary", "true"); + xml::add_element($doc, $ims, "poco:type", "xmpp"); + xml::add_element($doc, $ims, "poco:value", $xmpp); + xml::add_element($doc, $ims, "poco:primary", "true"); $author->appendChild($ims); } if (trim($profile["locality"].$profile["region"].$profile["country-name"]) != "") { $element = $doc->createElement("poco:address"); - xml_add_element($doc, $element, "poco:formatted", formatted_location($profile)); + xml::add_element($doc, $element, "poco:formatted", formatted_location($profile)); if (trim($profile["locality"]) != "") - xml_add_element($doc, $element, "poco:locality", $profile["locality"]); + xml::add_element($doc, $element, "poco:locality", $profile["locality"]); if (trim($profile["region"]) != "") - xml_add_element($doc, $element, "poco:region", $profile["region"]); + xml::add_element($doc, $element, "poco:region", $profile["region"]); if (trim($profile["country-name"]) != "") - xml_add_element($doc, $element, "poco:country", $profile["country-name"]); + xml::add_element($doc, $element, "poco:country", $profile["country-name"]); $author->appendChild($element); } @@ -578,9 +579,9 @@ class dfrn { $contact = get_contact_details_by_url($contact_url, $item["uid"]); $author = $doc->createElement($element); - xml_add_element($doc, $author, "name", $contact["name"]); - xml_add_element($doc, $author, "uri", $contact["url"]); - xml_add_element($doc, $author, "dfrn:handle", $contact["addr"]); + xml::add_element($doc, $author, "name", $contact["name"]); + xml::add_element($doc, $author, "uri", $contact["url"]); + xml::add_element($doc, $author, "dfrn:handle", $contact["addr"]); /// @Todo /// - Check real image type and image size @@ -591,7 +592,7 @@ class dfrn { "media:width" => 80, "media:height" => 80, "href" => $contact["photo"]); - xml_add_element($doc, $author, "link", "", $attributes); + xml::add_element($doc, $author, "link", "", $attributes); $attributes = array( "rel" => "avatar", @@ -599,7 +600,7 @@ class dfrn { "media:width" => 80, "media:height" => 80, "href" => $contact["photo"]); - xml_add_element($doc, $author, "link", "", $attributes); + xml::add_element($doc, $author, "link", "", $attributes); return $author; } @@ -622,28 +623,35 @@ class dfrn { if(!$r) return false; if($r->type) - xml_add_element($doc, $entry, "activity:object-type", $r->type); + xml::add_element($doc, $entry, "activity:object-type", $r->type); if($r->id) - xml_add_element($doc, $entry, "id", $r->id); + xml::add_element($doc, $entry, "id", $r->id); if($r->title) - xml_add_element($doc, $entry, "title", $r->title); + xml::add_element($doc, $entry, "title", $r->title); if($r->link) { - if(substr($r->link,0,1) === '<') { + if(substr($r->link,0,1) == '<') { if(strstr($r->link,'&') && (! strstr($r->link,'&'))) $r->link = str_replace('&','&', $r->link); $r->link = preg_replace('/\/','',$r->link); - $data = parse_xml_string($r->link, false); - foreach ($data->attributes() AS $parameter => $value) - $attributes[$parameter] = $value; - } else + // XML does need a single element as root element so we add a dummy element here + $data = parse_xml_string("".$r->link."", false); + if (is_object($data)) { + foreach ($data->link AS $link) { + $attributes = array(); + foreach ($link->attributes() AS $parameter => $value) + $attributes[$parameter] = $value; + xml::add_element($doc, $entry, "link", "", $attributes); + } + } + } else { $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $r->link); - - xml_add_element($doc, $entry, "link", "", $attributes); + xml::add_element($doc, $entry, "link", "", $attributes); + } } if($r->content) - xml_add_element($doc, $entry, "content", bbcode($r->content), array("type" => "html")); + xml::add_element($doc, $entry, "content", bbcode($r->content), array("type" => "html")); return $entry; } @@ -677,7 +685,7 @@ class dfrn { if(trim($matches[4]) != "") $attributes["title"] = trim($matches[4]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); } } } @@ -704,7 +712,7 @@ class dfrn { if($item['deleted']) { $attributes = array("ref" => $item['uri'], "when" => datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)); - return xml_create_element($doc, "at:deleted-entry", "", $attributes); + return xml::create_element($doc, "at:deleted-entry", "", $attributes); } $entry = $doc->createElement("entry"); @@ -714,6 +722,9 @@ class dfrn { else $body = $item['body']; + // Remove the abstract element. It is only locally important. + $body = remove_abstract($body); + if ($type == 'html') { $htmlbody = $body; @@ -735,66 +746,66 @@ class dfrn { $attributes = array("ref" => $parent_item, "type" => "text/html", "href" => app::get_baseurl().'/display/'.$parent[0]['guid'], "dfrn:diaspora_guid" => $parent[0]['guid']); - xml_add_element($doc, $entry, "thr:in-reply-to", "", $attributes); + xml::add_element($doc, $entry, "thr:in-reply-to", "", $attributes); } - xml_add_element($doc, $entry, "id", $item["uri"]); - xml_add_element($doc, $entry, "title", $item["title"]); + xml::add_element($doc, $entry, "id", $item["uri"]); + xml::add_element($doc, $entry, "title", $item["title"]); - xml_add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME)); - xml_add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME)); + xml::add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME)); + xml::add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME)); // "dfrn:env" is used to read the content - xml_add_element($doc, $entry, "dfrn:env", base64url_encode($body, true)); + xml::add_element($doc, $entry, "dfrn:env", base64url_encode($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_add_element($doc, $entry, "content", (($type === 'html') ? $htmlbody : $body), array("type" => $type)); + xml::add_element($doc, $entry, "content", (($type == 'html') ? $htmlbody : $body), array("type" => $type)); // We save this value in "plink". Maybe we should read it from there as well? - xml_add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html", + xml::add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html", "href" => app::get_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_add_element($doc, $entry, "dfrn:comment-allow", intval($item['last-child'])); + xml::add_element($doc, $entry, "dfrn:comment-allow", intval($item['last-child'])); if($item['location']) - xml_add_element($doc, $entry, "dfrn:location", $item['location']); + xml::add_element($doc, $entry, "dfrn:location", $item['location']); if($item['coord']) - xml_add_element($doc, $entry, "georss:point", $item['coord']); + xml::add_element($doc, $entry, "georss:point", $item['coord']); if(($item['private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) - xml_add_element($doc, $entry, "dfrn:private", (($item['private']) ? $item['private'] : 1)); + xml::add_element($doc, $entry, "dfrn:private", (($item['private']) ? $item['private'] : 1)); if($item['extid']) - xml_add_element($doc, $entry, "dfrn:extid", $item['extid']); + xml::add_element($doc, $entry, "dfrn:extid", $item['extid']); if($item['bookmark']) - xml_add_element($doc, $entry, "dfrn:bookmark", "true"); + xml::add_element($doc, $entry, "dfrn:bookmark", "true"); if($item['app']) - xml_add_element($doc, $entry, "statusnet:notice_info", "", array("local_id" => $item['id'], "source" => $item['app'])); + xml::add_element($doc, $entry, "statusnet:notice_info", "", array("local_id" => $item['id'], "source" => $item['app'])); - xml_add_element($doc, $entry, "dfrn:diaspora_guid", $item["guid"]); + xml::add_element($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(array('signed_text' => $item['signed_text'],'signature' => $item['signature'],'signer' => $item['signer']))); - xml_add_element($doc, $entry, "dfrn:diaspora_signature", $sign); + xml::add_element($doc, $entry, "dfrn:diaspora_signature", $sign); } - xml_add_element($doc, $entry, "activity:verb", construct_verb($item)); + xml::add_element($doc, $entry, "activity:verb", construct_verb($item)); if ($item['object-type'] != "") - xml_add_element($doc, $entry, "activity:object-type", $item['object-type']); + xml::add_element($doc, $entry, "activity:object-type", $item['object-type']); elseif ($item['id'] == $item['parent']) - xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); + xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); else - xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_COMMENT); + xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_COMMENT); $actobj = self::create_activity($doc, "activity:object", $item['object']); if ($actobj) @@ -809,7 +820,7 @@ class dfrn { if(count($tags)) { foreach($tags as $t) if (($type != 'html') OR ($t[0] != "@")) - xml_add_element($doc, $entry, "category", "", array("scheme" => "X-DFRN:".$t[0].":".$t[1], "term" => $t[2])); + xml::add_element($doc, $entry, "category", "", array("scheme" => "X-DFRN:".$t[0].":".$t[1], "term" => $t[2])); } if(count($tags)) @@ -822,11 +833,11 @@ class dfrn { intval($owner["uid"]), dbesc(normalise_link($mention))); if ($r[0]["forum"] OR $r[0]["prv"]) - xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned", + xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", "ostatus:object-type" => ACTIVITY_OBJ_GROUP, "href" => $mention)); else - xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned", + xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", "ostatus:object-type" => ACTIVITY_OBJ_PERSON, "href" => $mention)); } @@ -846,7 +857,7 @@ class dfrn { * * @return int Deliver status. -1 means an error. */ - function deliver($owner,$contact,$atom, $dissolve = false) { + public static function deliver($owner,$contact,$atom, $dissolve = false) { $a = get_app(); @@ -1108,13 +1119,13 @@ class dfrn { * * @return Returns an array with relevant data of the author */ - private function fetchauthor($xpath, $context, $importer, $element, $onlyfetch) { + private function fetchauthor($xpath, $context, $importer, $element, $onlyfetch, $xml = "") { $author = array(); $author["name"] = $xpath->evaluate($element."/atom:name/text()", $context)->item(0)->nodeValue; $author["link"] = $xpath->evaluate($element."/atom:uri/text()", $context)->item(0)->nodeValue; - $r = q("SELECT `id`, `uid`, `network`, `avatar-date`, `name-date`, `uri-date`, `addr`, + $r = q("SELECT `id`, `uid`, `url`, `network`, `avatar-date`, `name-date`, `uri-date`, `addr`, `name`, `nick`, `about`, `location`, `keywords`, `bdyear`, `bd` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'", intval($importer["uid"]), dbesc(normalise_link($author["link"])), dbesc(NETWORK_STATUSNET)); @@ -1123,6 +1134,9 @@ class dfrn { $author["contact-id"] = $r[0]["id"]; $author["network"] = $r[0]["network"]; } else { + if (!$onlyfetch) + logger("Contact ".$author["link"]." wasn't found for user ".$importer["uid"]." XML: ".$xml, LOGGER_DEBUG); + $author["contact-id"] = $importer["id"]; $author["network"] = $importer["network"]; $onlyfetch = true; @@ -1152,38 +1166,41 @@ class dfrn { } if ($r AND !$onlyfetch) { + logger("Check if contact details for contact ".$r[0]["id"]." (".$r[0]["nick"].") have to be updated.", LOGGER_DEBUG); + + $poco = array("url" => $contact["url"]); // When was the last change to name or uri? $name_element = $xpath->query($element."/atom:name", $context)->item(0); foreach($name_element->attributes AS $attributes) if ($attributes->name == "updated") - $contact["name-date"] = $attributes->textContent; + $poco["name-date"] = $attributes->textContent; $link_element = $xpath->query($element."/atom:link", $context)->item(0); foreach($link_element->attributes AS $attributes) if ($attributes->name == "updated") - $contact["uri-date"] = $attributes->textContent; + $poco["uri-date"] = $attributes->textContent; // Update contact data $value = $xpath->evaluate($element."/dfrn:handle/text()", $context)->item(0)->nodeValue; if ($value != "") - $contact["addr"] = $value; + $poco["addr"] = $value; $value = $xpath->evaluate($element."/poco:displayName/text()", $context)->item(0)->nodeValue; if ($value != "") - $contact["name"] = $value; + $poco["name"] = $value; $value = $xpath->evaluate($element."/poco:preferredUsername/text()", $context)->item(0)->nodeValue; if ($value != "") - $contact["nick"] = $value; + $poco["nick"] = $value; $value = $xpath->evaluate($element."/poco:note/text()", $context)->item(0)->nodeValue; if ($value != "") - $contact["about"] = $value; + $poco["about"] = $value; $value = $xpath->evaluate($element."/poco:address/poco:formatted/text()", $context)->item(0)->nodeValue; if ($value != "") - $contact["location"] = $value; + $poco["location"] = $value; /// @todo Add support for the following fields that we don't support by now in the contact table: /// - poco:utcOffset @@ -1200,7 +1217,7 @@ class dfrn { $tags[$tag->nodeValue] = $tag->nodeValue; if (count($tags)) - $contact["keywords"] = implode(", ", $tags); + $poco["keywords"] = implode(", ", $tags); // "dfrn:birthday" contains the birthday converted to UTC $old_bdyear = $contact["bdyear"]; @@ -1210,7 +1227,7 @@ class dfrn { if (strtotime($birthday) > time()) { $bd_timestamp = strtotime($birthday); - $contact["bdyear"] = date("Y", $bd_timestamp); + $poco["bdyear"] = date("Y", $bd_timestamp); } // "poco:birthday" is the birthday in the format "yyyy-mm-dd" @@ -1225,9 +1242,11 @@ class dfrn { $bdyear = $bdyear + 1; } - $contact["bd"] = $value; + $poco["bd"] = $value; } + $contact = array_merge($contact, $poco); + if ($old_bdyear != $contact["bdyear"]) self::birthday_event($contact, $birthday); @@ -1238,6 +1257,7 @@ class dfrn { unset($fields["id"]); unset($fields["uid"]); + unset($fields["url"]); unset($fields["avatar-date"]); unset($fields["name-date"]); unset($fields["uri-date"]); @@ -1245,8 +1265,10 @@ class dfrn { // Update check for this field has to be done differently $datefields = array("name-date", "uri-date"); foreach ($datefields AS $field) - if (strtotime($contact[$field]) > strtotime($r[0][$field])) + if (strtotime($contact[$field]) > strtotime($r[0][$field])) { + logger("Difference for contact ".$contact["id"]." in field '".$field."'. Old value: '".$contact[$field]."', new value '".$r[0][$field]."'", LOGGER_DEBUG); $update = true; + } foreach ($fields AS $field => $data) if ($contact[$field] != $r[0][$field]) { @@ -1255,7 +1277,7 @@ class dfrn { } if ($update) { - logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG); + logger("Update contact data for contact ".$contact["id"]." (".$contact["nick"].")", LOGGER_DEBUG); q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s', `addr` = '%s', `keywords` = '%s', `bdyear` = '%s', `bd` = '%s', @@ -1274,9 +1296,10 @@ class dfrn { // It is used in the socgraph.php to prevent that old contact data // that was relayed over several servers can overwrite contact // data that we received directly. - $contact["generation"] = 2; - $contact["photo"] = $author["avatar"]; - update_gcontact($contact); + + $poco["generation"] = 2; + $poco["photo"] = $author["avatar"]; + update_gcontact($poco); } return($author); @@ -1301,7 +1324,7 @@ class dfrn { $obj_element = $obj_doc->createElementNS(NAMESPACE_ATOM1, $element); $activity_type = $xpath->query("activity:object-type/text()", $activity)->item(0)->nodeValue; - xml_add_element($obj_doc, $obj_element, "type", $activity_type); + xml::add_element($obj_doc, $obj_element, "type", $activity_type); $id = $xpath->query("atom:id", $activity)->item(0); if (is_object($id)) @@ -1311,9 +1334,10 @@ class dfrn { if (is_object($title)) $obj_element->appendChild($obj_doc->importNode($title, true)); - $link = $xpath->query("atom:link", $activity)->item(0); - if (is_object($link)) - $obj_element->appendChild($obj_doc->importNode($link, true)); + $links = $xpath->query("atom:link", $activity); + if (is_object($links)) + foreach ($links AS $link) + $obj_element->appendChild($obj_doc->importNode($link, true)); $content = $xpath->query("atom:content", $activity)->item(0); if (is_object($content)) @@ -1739,6 +1763,151 @@ class dfrn { } } + /** + * @brief Processes several actions, depending on the verb + * + * @param int $entrytype Is it a toplevel entry, a comment or a relayed comment? + * @param array $importer Record of the importer user mixed with contact of the content + * @param array $item the new item record + * @param bool $is_like Is the verb a "like"? + * + * @return bool Should the processing of the entries be continued? + */ + private function process_verbs($entrytype, $importer, &$item, &$is_like) { + + logger("Process verb ".$item["verb"]." and object-type ".$item["object-type"]." for entrytype ".$entrytype, LOGGER_DEBUG); + + if (($entrytype == DFRN_TOP_LEVEL)) { + // The filling of the the "contact" variable is done for legcy reasons + // The functions below are partly used by ostatus.php as well - where we have this variable + $r = q("SELECT * FROM `contact` WHERE `id` = %d", intval($importer["id"])); + $contact = $r[0]; + $nickname = $contact["nick"]; + + // 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)) { + logger("New follower"); + new_follower($importer, $contact, $item, $nickname); + return false; + } + if(activity_match($item["verb"],ACTIVITY_UNFOLLOW)) { + logger("Lost follower"); + lose_follower($importer, $contact, $item); + return false; + } + if(activity_match($item["verb"],ACTIVITY_REQ_FRIEND)) { + logger("New friend request"); + new_follower($importer, $contact, $item, $nickname, true); + return false; + } + if(activity_match($item["verb"],ACTIVITY_UNFRIEND)) { + logger("Lost sharer"); + lose_sharer($importer, $contact, $item); + return false; + } + } else { + if(($item["verb"] == ACTIVITY_LIKE) + || ($item["verb"] == ACTIVITY_DISLIKE) + || ($item["verb"] == ACTIVITY_ATTEND) + || ($item["verb"] == ACTIVITY_ATTENDNO) + || ($item["verb"] == ACTIVITY_ATTENDMAYBE)) { + $is_like = true; + $item["type"] = "activity"; + $item["gravity"] = GRAVITY_LIKE; + // only one like or dislike per person + // splitted into two queries for performance issues + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `parent-uri` = '%s' AND NOT `deleted` LIMIT 1", + intval($item["uid"]), + dbesc($item["author-link"]), + dbesc($item["verb"]), + dbesc($item["parent-uri"]) + ); + if($r && count($r)) + return false; + + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `thr-parent` = '%s' AND NOT `deleted` LIMIT 1", + intval($item["uid"]), + dbesc($item["author-link"]), + dbesc($item["verb"]), + dbesc($item["parent-uri"]) + ); + if($r && count($r)) + return false; + } else + $is_like = false; + + if(($item["verb"] == ACTIVITY_TAG) && ($item["object-type"] == ACTIVITY_OBJ_TAGTERM)) { + + $xo = parse_xml_string($item["object"],false); + $xt = parse_xml_string($item["target"],false); + + if($xt->type == ACTIVITY_OBJ_NOTE) { + $r = q("SELECT `id`, `tag` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($xt->id), + intval($importer["importer_uid"]) + ); + + if(!count($r)) + return false; + + // extract tag, if not duplicate, add to parent item + if($xo->content) { + if(!(stristr($r[0]["tag"],trim($xo->content)))) { + q("UPDATE `item` SET `tag` = '%s' WHERE `id` = %d", + dbesc($r[0]["tag"] . (strlen($r[0]["tag"]) ? ',' : '') . '#[url=' . $xo->id . ']'. $xo->content . '[/url]'), + intval($r[0]["id"]) + ); + create_tags_from_item($r[0]["id"]); + } + } + } + } + } + return true; + } + + /** + * @brief Processes the link elements + * + * @param object $links link elements + * @param array $item the item record + */ + private function parse_links($links, &$item) { + $rel = ""; + $href = ""; + $type = ""; + $length = "0"; + $title = ""; + foreach ($links AS $link) { + foreach($link->attributes AS $attributes) { + if ($attributes->name == "href") + $href = $attributes->textContent; + if ($attributes->name == "rel") + $rel = $attributes->textContent; + if ($attributes->name == "type") + $type = $attributes->textContent; + if ($attributes->name == "length") + $length = $attributes->textContent; + if ($attributes->name == "title") + $title = $attributes->textContent; + } + if (($rel != "") AND ($href != "")) + switch($rel) { + case "alternate": + $item["plink"] = $href; + break; + case "enclosure": + $enclosure = $href; + if(strlen($item["attach"])) + $item["attach"] .= ","; + + $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]'; + break; + } + } + } + /** * @brief Processes the entry elements which contain the items and comments * @@ -1803,6 +1972,8 @@ class dfrn { $item['body'] = @html2bbcode($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; @@ -1855,54 +2026,36 @@ class dfrn { $categories = $xpath->query("atom:category", $entry); if ($categories) { foreach ($categories AS $category) { - foreach($category->attributes AS $attributes) - if ($attributes->name == "term") { + $term = ""; + $scheme = ""; + foreach($category->attributes AS $attributes) { + if ($attributes->name == "term") $term = $attributes->textContent; + + if ($attributes->name == "scheme") + $scheme = $attributes->textContent; + } + + if (($term != "") AND ($scheme != "")) { + $parts = explode(":", $scheme); + if ((count($parts) >= 4) AND (array_shift($parts) == "X-DFRN")) { + $termhash = array_shift($parts); + $termurl = implode(":", $parts); + if(strlen($item["tag"])) $item["tag"] .= ","; - $item["tag"] .= "#[url=".App::get_baseurl()."/search?tag=".$term."]".$term."[/url]"; + $item["tag"] .= $termhash."[url=".$termurl."]".$term."[/url]"; } + } } } $enclosure = ""; $links = $xpath->query("atom:link", $entry); - if ($links) { - $rel = ""; - $href = ""; - $type = ""; - $length = "0"; - $title = ""; - foreach ($links AS $link) { - foreach($link->attributes AS $attributes) { - if ($attributes->name == "href") - $href = $attributes->textContent; - if ($attributes->name == "rel") - $rel = $attributes->textContent; - if ($attributes->name == "type") - $type = $attributes->textContent; - if ($attributes->name == "length") - $length = $attributes->textContent; - if ($attributes->name == "title") - $title = $attributes->textContent; - } - if (($rel != "") AND ($href != "")) - switch($rel) { - case "alternate": - $item["plink"] = $href; - break; - case "enclosure": - $enclosure = $href; - if(strlen($item["attach"])) - $item["attach"] .= ","; - - $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]'; - break; - } - } - } + if ($links) + self::parse_links($links, $item); // Is it a reply or a top level posting? $item["parent-uri"] = $item["uri"]; @@ -1932,6 +2085,15 @@ class dfrn { if (($item["network"] != $author["network"]) AND ($author["network"] != "")) $item["network"] = $author["network"]; + + // This code was taken from the old DFRN code + // When activated, forums don't work. + // And: Why should we disallow commenting by followers? + // the behaviour is now similar to the Diaspora part. + //if($importer["rel"] == CONTACT_IS_FOLLOWER) { + // logger("Contact ".$importer["id"]." is only follower. Quitting", LOGGER_DEBUG); + // return; + //} } if ($entrytype == DFRN_REPLY_RC) { @@ -1941,6 +2103,7 @@ class dfrn { if (!isset($item["object-type"])) $item["object-type"] = ACTIVITY_OBJ_NOTE; + // Is it an event? if ($item["object-type"] == ACTIVITY_OBJ_EVENT) { logger("Item ".$item["uri"]." seems to contain an event.", LOGGER_DEBUG); $ev = bbtoevent($item["body"]); @@ -1965,35 +2128,6 @@ class dfrn { return; } } - - // The filling of the the "contact" variable is done for legcy reasons - // The functions below are partly used by ostatus.php as well - where we have this variable - $r = q("SELECT * FROM `contact` WHERE `id` = %d", intval($importer["id"])); - $contact = $r[0]; - $nickname = $contact["nick"]; - - // 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)) { - logger('New follower'); - new_follower($importer, $contact, $item, $nickname); - return; - } - if(activity_match($item['verb'],ACTIVITY_UNFOLLOW)) { - logger('Lost follower'); - lose_follower($importer, $contact, $item); - return; - } - if(activity_match($item['verb'],ACTIVITY_REQ_FRIEND)) { - logger('New friend request'); - new_follower($importer, $contact, $item, $nickname, true); - return; - } - if(activity_match($item['verb'],ACTIVITY_UNFRIEND)) { - logger('Lost sharer'); - lose_sharer($importer, $contact, $item); - return; - } } $r = q("SELECT `id`, `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", @@ -2001,6 +2135,11 @@ class dfrn { intval($importer["importer_uid"]) ); + if (!self::process_verbs($entrytype, $importer, $item, $is_like)) { + logger("Exiting because 'process_verbs' told us so", LOGGER_DEBUG); + return; + } + // Update content if 'updated' changes if(count($r)) { if (self::update_content($r[0], $item, $importer, $entrytype)) @@ -2011,69 +2150,6 @@ class dfrn { } if (in_array($entrytype, array(DFRN_REPLY, DFRN_REPLY_RC))) { - if($importer["rel"] == CONTACT_IS_FOLLOWER) { - logger("Contact ".$importer["id"]." is only follower. Quitting", LOGGER_DEBUG); - return; - } - - if(($item["verb"] === ACTIVITY_LIKE) - || ($item["verb"] === ACTIVITY_DISLIKE) - || ($item["verb"] === ACTIVITY_ATTEND) - || ($item["verb"] === ACTIVITY_ATTENDNO) - || ($item["verb"] === ACTIVITY_ATTENDMAYBE)) { - $is_like = true; - $item["type"] = "activity"; - $item["gravity"] = GRAVITY_LIKE; - // only one like or dislike per person - // splitted into two queries for performance issues - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `parent-uri` = '%s' AND NOT `deleted` LIMIT 1", - intval($item["uid"]), - dbesc($item["author-link"]), - dbesc($item["verb"]), - dbesc($item["parent-uri"]) - ); - if($r && count($r)) - return; - - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `thr-parent` = '%s' AND NOT `deleted` LIMIT 1", - intval($item["uid"]), - dbesc($item["author-link"]), - dbesc($item["verb"]), - dbesc($item["parent-uri"]) - ); - if($r && count($r)) - return; - - } else - $is_like = false; - - if(($item["verb"] === ACTIVITY_TAG) && ($item["object-type"] === ACTIVITY_OBJ_TAGTERM)) { - - $xo = parse_xml_string($item["object"],false); - $xt = parse_xml_string($item["target"],false); - - if($xt->type == ACTIVITY_OBJ_NOTE) { - $r = q("SELECT `id`, `tag` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($xt->id), - intval($importer["importer_uid"]) - ); - - if(!count($r)) - return; - - // extract tag, if not duplicate, add to parent item - if($xo->content) { - if(!(stristr($r[0]["tag"],trim($xo->content)))) { - q("UPDATE `item` SET `tag` = '%s' WHERE `id` = %d", - dbesc($r[0]["tag"] . (strlen($r[0]["tag"]) ? ',' : '') . '#[url=' . $xo->id . ']'. $xo->content . '[/url]'), - intval($r[0]["id"]) - ); - create_tags_from_item($r[0]["id"]); - } - } - } - } - $posted_id = item_store($item); $parent = 0; @@ -2113,7 +2189,7 @@ class dfrn { return true; } - } else { + } else { // $entrytype == DFRN_TOP_LEVEL if(!link_compare($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. @@ -2189,15 +2265,17 @@ class dfrn { else return; - if($item["object-type"] === ACTIVITY_OBJ_EVENT) { + if($item["object-type"] == ACTIVITY_OBJ_EVENT) { logger("Deleting event ".$item["event-id"], LOGGER_DEBUG); event_delete($item["event-id"]); } - if(($item["verb"] === ACTIVITY_TAG) && ($item["object-type"] === ACTIVITY_OBJ_TAGTERM)) { + if(($item["verb"] == ACTIVITY_TAG) && ($item["object-type"] == ACTIVITY_OBJ_TAGTERM)) { + $xo = parse_xml_string($item["object"],false); $xt = parse_xml_string($item["target"],false); - if($xt->type === ACTIVITY_OBJ_NOTE) { + + if($xt->type == ACTIVITY_OBJ_NOTE) { $i = q("SELECT `id`, `contact-id`, `tag` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($xt->id), intval($importer["importer_uid"]) @@ -2290,7 +2368,7 @@ class dfrn { * @param array $importer Record of the importer user mixed with contact of the content * @param bool $sort_by_date Is used when feeds are polled */ - function import($xml,$importer, $sort_by_date = false) { + public static function import($xml,$importer, $sort_by_date = false) { if ($xml == "") return; @@ -2326,8 +2404,14 @@ class dfrn { $header["contact-id"] = $importer["id"]; // 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); + // Only the "dfrn:owner" in the head section contains all data - self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false); + if ($xpath->query("/atom:feed/dfrn:owner")->length > 0) + self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false, $xml); logger("Import DFRN message for user ".$importer["uid"]." from contact ".$importer["id"], LOGGER_DEBUG);