use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Model\Mail;
+use Friendica\Model\Notify;
use Friendica\Model\Notify\Type;
use Friendica\Model\PermissionSet;
+use Friendica\Model\Post;
use Friendica\Model\Post\Category;
use Friendica\Model\Profile;
use Friendica\Model\Tag;
use Friendica\Model\User;
+use Friendica\Model\Verb;
use Friendica\Network\Probe;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
FROM `item` USE INDEX (`uid_wall_changed`) $sql_post_table
STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
WHERE `item`.`uid` = %d AND `item`.`wall` AND `item`.`changed` > '%s'
- AND `item`.`visible` $sql_extra
+ AND `vid` != %d AND `item`.`visible` $sql_extra
ORDER BY `item`.`parent` ".$sort.", `item`.`received` ASC LIMIT 0, 300",
intval($owner_id),
DBA::escape($check_date),
+ Verb::getID(Activity::ANNOUNCE),
DBA::escape($sort)
);
/**
* Create XML text for DFRN mails
*
- * @param array $item message elements
+ * @param array $mail Mail record
* @param array $owner Owner record
*
* @return string DFRN mail
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Find proper type-hints
*/
- public static function mail($item, $owner)
+ public static function mail(array $mail, array $owner)
{
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = self::addHeader($doc, $owner, "dfrn:owner", "", false);
- $mail = $doc->createElement("dfrn:mail");
- $sender = $doc->createElement("dfrn:sender");
+ $mailElement = $doc->createElement("dfrn:mail");
+ $senderElement = $doc->createElement("dfrn:sender");
- XML::addElement($doc, $sender, "dfrn:name", $owner['name']);
- XML::addElement($doc, $sender, "dfrn:uri", $owner['url']);
- XML::addElement($doc, $sender, "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']);
- $mail->appendChild($sender);
+ $mailElement->appendChild($senderElement);
- XML::addElement($doc, $mail, "dfrn:id", $item['uri']);
- XML::addElement($doc, $mail, "dfrn:in-reply-to", $item['parent-uri']);
- XML::addElement($doc, $mail, "dfrn:sentdate", DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM));
- XML::addElement($doc, $mail, "dfrn:subject", $item['title']);
- XML::addElement($doc, $mail, "dfrn:content", $item['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($mail);
+ $root->appendChild($mailElement);
return trim($doc->saveXML());
}
*/
private static function getAttachment($doc, $root, $item)
{
- $arr = explode('[/attach],', $item['attach']);
- if (count($arr)) {
- foreach ($arr as $r) {
- $matches = false;
- $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|', $r, $matches);
- if ($cnt) {
- $attributes = ["rel" => "enclosure",
- "href" => $matches[1],
- "type" => $matches[3]];
-
- if (intval($matches[2])) {
- $attributes["length"] = intval($matches[2]);
- }
-
- if (trim($matches[4]) != "") {
- $attributes["title"] = trim($matches[4]);
- }
+ foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT, Post\Media::TORRENT, Post\Media::UNKNOWN]) as $attachment) {
+ $attributes = ['rel' => 'enclosure',
+ 'href' => $attachment['url'],
+ 'type' => $attachment['mimetype']];
- XML::addElement($doc, $root, "link", "", $attributes);
- }
+ if (!empty($attachment['size'])) {
+ $attributes['length'] = intval($attachment['size']);
}
+ if (!empty($attachment['description'])) {
+ $attributes['title'] = $attachment['description'];
+ }
+
+ XML::addElement($doc, $root, 'link', '', $attributes);
}
}
$entry->appendChild($dfrnowner);
if ($item['gravity'] != GRAVITY_PARENT) {
- $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']);
- $parent = Item::selectFirst(['guid', 'plink'], ['uri' => $parent_item, 'uid' => $item['uid']]);
- $attributes = ["ref" => $parent_item, "type" => "text/html",
- "href" => $parent['plink'],
- "dfrn:diaspora_guid" => $parent['guid']];
- XML::addElement($doc, $entry, "thr:in-reply-to", "", $attributes);
+ $parent = Item::selectFirst(['guid', 'plink'], ['uri' => $item['thr-parent'], 'uid' => $item['uid']]);
+ if (DBA::isResult($parent)) {
+ $attributes = ["ref" => $item['thr-parent'], "type" => "text/html",
+ "href" => $parent['plink'],
+ "dfrn:diaspora_guid" => $parent['guid']];
+ XML::addElement($doc, $entry, "thr:in-reply-to", "", $attributes);
+ }
}
// Add conversation data. This is used for OStatus
$conversation_uri = $conversation_href;
if (isset($parent_item)) {
- $conversation = DBA::selectFirst('conversation', ['conversation-uri', 'conversation-href'], ['item-uri' => $item['parent-uri']]);
+ $conversation = DBA::selectFirst('conversation', ['conversation-uri', 'conversation-href'], ['item-uri' => $item['thr-parent']]);
if (DBA::isResult($conversation)) {
if ($conversation['conversation-uri'] != '') {
$conversation_uri = $conversation['conversation-uri'];
$fields = ['id', 'uid', 'url', 'network', 'avatar-date', 'avatar', 'name-date', 'uri-date', 'addr',
'name', 'nick', 'about', 'location', 'keywords', 'xmpp', 'bdyear', 'bd', 'hidden', 'contact-type'];
- $condition = ["`uid` = ? AND `nurl` = ? AND `network` != ?",
+ $condition = ["`uid` = ? AND `nurl` = ? AND `network` != ? AND NOT `pending` AND NOT `blocked`",
$importer["importer_uid"], Strings::normaliseLink($author["link"]), Protocol::STATUSNET];
+
+ if ($importer['account-type'] != User::ACCOUNT_TYPE_COMMUNITY) {
+ $condition = DBA::mergeConditions($condition, ['rel' => [Contact::SHARING, Contact::FRIEND]]);
+ }
+
$contact_old = DBA::selectFirst('contact', $fields, $condition);
if (DBA::isResult($contact_old)) {
$author["contact-id"] = $contact_old["id"];
$author["network"] = $contact_old["network"];
} else {
- if (!$onlyfetch) {
- Logger::debug("Contact ".$author["link"]." wasn't found for user ".$importer["importer_uid"]." XML: ".$xml);
- }
+ Logger::info('Contact not found', ['condition' => $condition]);
$author["contact-unknown"] = true;
- $author["contact-id"] = $importer["id"];
- $author["network"] = $importer["network"];
+ $contact = Contact::getByURL($author["link"], null, ["id", "network"]);
+ $author["contact-id"] = $contact["id"] ?? $importer["id"];
+ $author["network"] = $contact["network"] ?? $importer["network"];
$onlyfetch = true;
}
$msg = [];
$msg["uid"] = $importer["importer_uid"];
- $msg["from-name"] = $xpath->query("dfrn:sender/dfrn:name/text()", $mail)->item(0)->nodeValue;
- $msg["from-url"] = $xpath->query("dfrn:sender/dfrn:uri/text()", $mail)->item(0)->nodeValue;
- $msg["from-photo"] = $xpath->query("dfrn:sender/dfrn:avatar/text()", $mail)->item(0)->nodeValue;
+ $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"] = $xpath->query("dfrn:id/text()", $mail)->item(0)->nodeValue;
- $msg["parent-uri"] = $xpath->query("dfrn:in-reply-to/text()", $mail)->item(0)->nodeValue;
- $msg["created"] = DateTimeFormat::utc($xpath->query("dfrn:sentdate/text()", $mail)->item(0)->nodeValue);
- $msg["title"] = $xpath->query("dfrn:subject/text()", $mail)->item(0)->nodeValue;
- $msg["body"] = $xpath->query("dfrn:content/text()", $mail)->item(0)->nodeValue;
+ $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);
}
*/
private static function processSuggestion($xpath, $suggestion, $importer)
{
- Logger::log('Processing suggestions');
-
- /// @TODO Rewrite this to one statement
- $suggest = [];
- $suggest['uid'] = $importer['importer_uid'];
- $suggest['cid'] = $importer['id'];
- $suggest['url'] = $xpath->query('dfrn:url/text()', $suggestion)->item(0)->nodeValue;
- $suggest['name'] = $xpath->query('dfrn:name/text()', $suggestion)->item(0)->nodeValue;
- $suggest['photo'] = $xpath->query('dfrn:photo/text()', $suggestion)->item(0)->nodeValue;
- $suggest['request'] = $xpath->query('dfrn:request/text()', $suggestion)->item(0)->nodeValue;
- $suggest['body'] = $xpath->query('dfrn:note/text()', $suggestion)->item(0)->nodeValue;
+ Logger::notice('Processing suggestions');
- // Does our member already have a friend matching this description?
+ $url = $xpath->evaluate('string(dfrn:url[1]/text())', $suggestion);
+ $cid = Contact::getIdForURL($url);
+ $note = $xpath->evaluate('string(dfrn:note[1]/text())', $suggestion);
- /*
- * The valid result means the friend we're about to send a friend
- * suggestion already has them in their contact, which means no further
- * action is required.
- *
- * @see https://github.com/friendica/friendica/pull/3254#discussion_r107315246
- */
- $condition = ['nurl' => Strings::normaliseLink($suggest['url']), 'uid' => $suggest['uid']];
- if (DBA::exists('contact', $condition)) {
- return false;
- }
- // Do we already have an fcontact record for this person?
-
- $fid = 0;
- $fcontact = DBA::selectFirst('fcontact', ['id'], ['url' => $suggest['url']]);
- if (DBA::isResult($fcontact)) {
- $fid = $fcontact['id'];
-
- // OK, we do. Do we already have an introduction for this person?
- if (DBA::exists('intro', ['uid' => $suggest['uid'], 'fid' => $fid])) {
- /*
- * The valid result means the friend we're about to send a friend
- * suggestion already has them in their contact, which means no further
- * action is required.
- *
- * @see https://github.com/friendica/friendica/pull/3254#discussion_r107315246
- */
- return false;
- }
- }
-
- if (!$fid) {
- $fields = ['name' => $suggest['name'], 'url' => $suggest['url'],
- 'photo' => $suggest['photo'], 'request' => $suggest['request']];
- DBA::insert('fcontact', $fields);
- $fid = DBA::lastInsertId();
- }
-
- /*
- * If no record in fcontact is found, below INSERT statement will not
- * link an introduction to it.
- */
- if (empty($fid)) {
- // Database record did not get created. Quietly give up.
- exit();
- }
-
- $hash = Strings::getRandomHex();
-
- $fields = ['uid' => $suggest['uid'], 'fid' => $fid, 'contact-id' => $suggest['cid'],
- 'note' => $suggest['body'], 'hash' => $hash, 'datetime' => DateTimeFormat::utcNow(), 'blocked' => false];
- DBA::insert('intro', $fields);
-
- notification(
- [
- 'type' => Type::SUGGEST,
- 'notify_flags' => $importer['notify-flags'],
- 'language' => $importer['language'],
- 'to_name' => $importer['username'],
- 'to_email' => $importer['email'],
- 'uid' => $importer['importer_uid'],
- 'item' => $suggest,
- 'link' => DI::baseUrl().'/notifications/intros',
- 'source_name' => $importer['name'],
- 'source_link' => $importer['url'],
- 'source_photo' => $importer['photo'],
- 'verb' => Activity::REQ_FRIEND,
- 'otype' => 'intro']
- );
-
- return true;
+ return FContact::addSuggestion($importer['importer_uid'], $cid, $importer['id'], $note);
}
/**
*/
private static function getEntryType($importer, $item)
{
- if ($item["parent-uri"] != $item["uri"]) {
+ if ($item["thr-parent"] != $item["uri"]) {
$community = false;
if ($importer["page-flags"] == User::PAGE_FLAGS_COMMUNITY || $importer["page-flags"] == User::PAGE_FLAGS_PRVGROUP) {
$is_a_remote_action = false;
- $parent = Item::selectFirst(['parent-uri'], ['uri' => $item["parent-uri"]]);
+ $parent = Item::selectFirst(['thr-parent'], ['uri' => $item["thr-parent"]]);
if (DBA::isResult($parent)) {
$r = q(
"SELECT `item`.`forum_mode`, `item`.`wall` FROM `item`
INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
- WHERE `item`.`uri` = '%s' AND (`item`.`parent-uri` = '%s' OR `item`.`thr-parent` = '%s')
+ WHERE `item`.`uri` = '%s' AND (`item`.`thr-parent` = '%s' OR `item`.`thr-parent` = '%s')
AND `item`.`uid` = %d
$sql_extra
LIMIT 1",
- DBA::escape($parent["parent-uri"]),
- DBA::escape($parent["parent-uri"]),
- DBA::escape($parent["parent-uri"]),
+ DBA::escape($parent["thr-parent"]),
+ DBA::escape($parent["thr-parent"]),
+ DBA::escape($parent["thr-parent"]),
intval($importer["importer_uid"])
);
if (DBA::isResult($r)) {
}
if ($Blink && Strings::compareLink($Blink, DI::baseUrl() . "/profile/" . $importer["nickname"])) {
- $author = DBA::selectFirst('contact', ['name', 'thumb', 'url'], ['id' => $item['author-id']]);
+ $author = DBA::selectFirst('contact', ['id', 'name', 'thumb', 'url'], ['id' => $item['author-id']]);
- $parent = Item::selectFirst(['id'], ['uri' => $item['parent-uri'], 'uid' => $importer["importer_uid"]]);
+ $parent = Item::selectFirst(['id'], ['uri' => $item['thr-parent'], 'uid' => $importer["importer_uid"]]);
$item['parent'] = $parent['id'];
// send a notification
notification(
[
- "type" => Type::POKE,
- "notify_flags" => $importer["notify-flags"],
- "language" => $importer["language"],
- "to_name" => $importer["username"],
- "to_email" => $importer["email"],
- "uid" => $importer["importer_uid"],
- "item" => $item,
- "link" => DI::baseUrl()."/display/".urlencode($item['guid']),
- "source_name" => $author["name"],
- "source_link" => $author["url"],
- "source_photo" => $author["thumb"],
- "verb" => $item["verb"],
- "otype" => "person",
- "activity" => $verb,
- "parent" => $item['parent']]
+ "type" => Type::POKE,
+ "otype" => Notify\ObjectType::PERSON,
+ "activity" => $verb,
+ "verb" => $item["verb"],
+ "uid" => $importer["importer_uid"],
+ "cid" => $author["id"],
+ "item" => $item,
+ "link" => DI::baseUrl() . "/display/" . urlencode($item['guid']),
+ ]
);
}
}
$is_like = true;
$item["gravity"] = GRAVITY_ACTIVITY;
// only one like or dislike per person
- // splitted into two queries for performance issues
+ // 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["parent-uri"]];
+ 'verb' => $item['verb'], 'parent-uri' => $item['thr-parent']];
if (Item::exists($condition)) {
return false;
}
$condition = ['uid' => $item["uid"], 'author-id' => $item["author-id"], 'gravity' => GRAVITY_ACTIVITY,
- 'verb' => $item["verb"], 'thr-parent' => $item["parent-uri"]];
+ 'verb' => $item['verb'], 'thr-parent' => $item['thr-parent']];
if (Item::exists($condition)) {
return false;
}
{
$rel = "";
$href = "";
- $type = "";
- $length = "0";
- $title = "";
+ $type = null;
+ $length = null;
+ $title = null;
foreach ($links as $link) {
foreach ($link->attributes as $attributes) {
switch ($attributes->name) {
$item["plink"] = $href;
break;
case "enclosure":
- if (!empty($item["attach"])) {
- $item["attach"] .= ",";
- } else {
- $item["attach"] = "";
- }
-
- $item["attach"] .= '[attach]href="' . $href . '" length="' . $length . '" type="' . $type . '" title="' . $title . '"[/attach]';
+ Post\Media::insert(['uri-id' => $item['uri-id'], 'type' => Post\Media::DOCUMENT,
+ 'url' => $href, 'mimetype' => $type, 'size' => $length, 'description' => $title]);
break;
}
}
}
}
+ /**
+ * Checks if an incoming message is wanted
+ *
+ * @param array $item
+ * @return boolean Is the message wanted?
+ */
+ private static function isSolicitedMessage(array $item)
+ {
+ if (DBA::exists('contact', ["`nurl` = ? AND `uid` != ? AND `rel` IN (?, ?)",
+ Strings::normaliseLink($item["author-link"]), 0, Contact::FRIEND, Contact::SHARING])) {
+ Logger::info('Author has got followers - accepted', ['uri' => $item['uri'], 'author' => $item["author-link"]]);
+ return true;
+ }
+
+ $taglist = Tag::getByURIId($item['uri-id'], [Tag::HASHTAG]);
+ $tags = array_column($taglist, 'name');
+ return Relay::isSolicitedPost($tags, $item['body'], $item['author-id'], $item['uri'], Protocol::DFRN);
+ }
+
/**
* Processes the entry elements which contain the items and comments
*
* @throws \ImagickException
* @todo Add type-hints
*/
- private static function processEntry($header, $xpath, $entry, $importer, $xml)
+ private static function processEntry($header, $xpath, $entry, $importer, $xml, $protocol)
{
Logger::log("Processing entries");
$item = $header;
- $item["protocol"] = Conversation::PARCEL_DFRN;
+ $item["protocol"] = $protocol;
$item["source"] = $xml;
$parts = explode(":", $scheme);
if ((count($parts) >= 4) && (array_shift($parts) == "X-DFRN")) {
$termurl = array_pop($parts);
- $termurl = array_pop($parts) . $termurl;
+ $termurl = array_pop($parts) . ':' . $termurl;
Tag::store($item['uri-id'], Tag::IMPLICIT_MENTION, $term, $termurl);
}
}
}
// Is it a reply or a top level posting?
- $item["parent-uri"] = $item["uri"];
+ $item['thr-parent'] = $item['uri'];
$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["parent-uri"] = $attributes->textContent;
+ $item['thr-parent'] = $attributes->textContent;
}
}
}
+ // Check if the message is wanted
+ if (($importer['importer_uid'] == 0) && ($item['uri'] == $item['thr-parent'])) {
+ if (!self::isSolicitedMessage($item)) {
+ DBA::delete('item-uri', ['uri' => $item['uri']]);
+ return 403;
+ }
+ }
+
// Get the type of the item (Top level post, reply or remote reply)
$entrytype = self::getEntryType($importer, $item);
}
if (in_array($entrytype, [DFRN::REPLY, DFRN::REPLY_RC])) {
+ // Will be overwritten for sharing accounts in Item::insert
+ if (empty($item['post-type']) && ($entrytype == DFRN::REPLY)) {
+ $item['post-type'] = Item::PT_COMMENT;
+ }
+
$posted_id = Item::insert($item);
if ($posted_id) {
Logger::log("Reply from contact ".$item["contact-id"]." was stored with id ".$posted_id, Logger::DEBUG);
* @throws \ImagickException
* @todo set proper type-hints
*/
- public static function import($xml, $importer, $sort_by_date = false)
+ public static function import($xml, $importer, $sort_by_date = false, $protocol = Conversation::PARCEL_DFRN)
{
if ($xml == "") {
return 400;
}
$deletions = $xpath->query("/atom:feed/at:deleted-entry");
- foreach ($deletions as $deletion) {
- self::processDeletion($xpath, $deletion, $importer);
+ if (!empty($deletions)) {
+ foreach ($deletions as $deletion) {
+ self::processDeletion($xpath, $deletion, $importer);
+ }
+ if (count($deletions) > 0) {
+ Logger::notice('Deletions had been processed');
+ return 200;
+ }
}
if (!$sort_by_date) {
$entries = $xpath->query("/atom:feed/atom:entry");
foreach ($entries as $entry) {
- self::processEntry($header, $xpath, $entry, $importer, $xml);
+ self::processEntry($header, $xpath, $entry, $importer, $xml, $protocol);
}
} else {
$newentries = [];
ksort($newentries);
foreach ($newentries as $entry) {
- self::processEntry($header, $xpath, $entry, $importer, $xml);
+ self::processEntry($header, $xpath, $entry, $importer, $xml, $protocol);
}
}
Logger::log("Import done for user " . $importer["importer_uid"] . " from contact " . $importer["id"], Logger::DEBUG);
// check that the message originated elsewhere and is a top-level post
- if ($item['wall'] || $item['origin'] || ($item['uri'] != $item['parent-uri'])) {
+ if ($item['wall'] || $item['origin'] || ($item['uri'] != $item['thr-parent'])) {
return false;
}