X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FDFRN.php;h=125d41fe654c74e6b5e307a0aab452d3e6245760;hb=fc4d1296e3a4d61a78438c94ad1216cd8b642027;hp=429c5051ffb4a9006e52d86f3f3b04785817a04f;hpb=96a6c098bccfc4cdd19c1be8a04a60b8a5e2f76b;p=friendica.git diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 429c5051ff..125d41fe65 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -31,6 +31,7 @@ use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\XML; +use Friendica\Protocol\Diaspora; use dba; use DOMDocument; use DOMXPath; @@ -62,7 +63,7 @@ class DFRN * @param array $owner Owner record * * @return string DFRN entries - * @todo Add type-hints + * @todo Find proper type-hints */ public static function entries($items, $owner) { @@ -119,8 +120,6 @@ class DFRN } } - - // default permissions - anonymous user $sql_extra = " AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' "; @@ -133,6 +132,7 @@ class DFRN ); if (! DBM::is_result($r)) { + logger(sprintf('No contact found for nickname=%d', $owner_nick), LOGGER_WARNING); killme(); } @@ -168,6 +168,7 @@ class DFRN ); if (! DBM::is_result($r)) { + logger(sprintf('No contact found for uid=%d', $owner_id), LOGGER_WARNING); killme(); } @@ -176,8 +177,10 @@ class DFRN $groups = Group::getIdsByContactId($contact['id']); if (count($groups)) { - for ($x = 0; $x < count($groups); $x ++) + for ($x = 0; $x < count($groups); $x ++) { $groups[$x] = '<' . intval($groups[$x]) . '>' ; + } + $gs = implode('|', $groups); } else { $gs = '<<>>' ; // Impossible to match @@ -396,7 +399,7 @@ class DFRN * @param array $owner Owner record * * @return string DFRN mail - * @todo Add type-hints + * @todo Find proper type-hints */ public static function mail($item, $owner) { @@ -432,7 +435,7 @@ class DFRN * @param array $owner Owner record * * @return string DFRN suggestions - * @todo Add type-hints + * @todo Find proper type-hints */ public static function fsuggest($item, $owner) { @@ -461,7 +464,7 @@ class DFRN * @param int $uid User ID * * @return string DFRN relocations - * @todo Add type-hints + * @todo Find proper type-hints */ public static function relocate($owner, $uid) { @@ -523,7 +526,7 @@ class DFRN * @param bool $public Is it a header for public posts? * * @return object XML root object - * @todo Add type-hints + * @todo Find proper type-hints */ private static function addHeader($doc, $owner, $authorelement, $alternatelink = "", $public = false) { @@ -599,7 +602,7 @@ class DFRN * @param boolean $public boolean * * @return object XML author object - * @todo Add type-hints + * @todo Find proper type-hints */ private static function addAuthor($doc, $owner, $authorelement, $public) { @@ -743,7 +746,7 @@ class DFRN * @param array $item Item elements * * @return object XML author object - * @todo Add type-hints + * @todo Find proper type-hints */ private static function addEntryAuthor($doc, $element, $contact_url, $item) { @@ -784,7 +787,7 @@ class DFRN * @param string $activity activity value * * @return object XML activity object - * @todo Add type-hints + * @todo Find proper type-hints */ private static function createActivity($doc, $element, $activity) { @@ -795,12 +798,15 @@ class DFRN if (!$r) { return false; } + if ($r->type) { XML::addElement($doc, $entry, "activity:object-type", $r->type); } + if ($r->id) { XML::addElement($doc, $entry, "id", $r->id); } + if ($r->title) { XML::addElement($doc, $entry, "title", $r->title); } @@ -847,7 +853,7 @@ class DFRN * @param array $item Item element * * @return object XML attachment object - * @todo Add type-hints + * @todo Find proper type-hints */ private static function getAttachment($doc, $root, $item) { @@ -887,7 +893,7 @@ class DFRN * @param bool $single If set, the entry is created as an XML document with a single "entry" element * * @return object XML entry object - * @todo Add type-hints + * @todo Find proper type-hints */ private static function entry($doc, $type, $item, $owner, $comment = false, $cid = 0, $single = false) { @@ -1145,13 +1151,20 @@ class DFRN * @param string $atom Content that will be transmitted * @param bool $dissolve (to be documented) * - * @return int Deliver status. -1 means an error. + * @return int Deliver status. Negative values mean an error. * @todo Add array type-hint for $owner, $contact */ public static function deliver($owner, $contact, $atom, $dissolve = false) { $a = get_app(); + // At first try the Diaspora transport layer + $ret = self::transmit($owner, $contact, $atom); + if ($ret >= 200) { + logger('Delivery via Diaspora transport layer was successful with status ' . $ret); + return $ret; + } + $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']); if ($contact['duplex'] && $contact['dfrn-id']) { @@ -1189,32 +1202,37 @@ class DFRN $ret = Network::curl($url); if ($ret['errno'] == CURLE_OPERATION_TIMEDOUT) { + Contact::markForArchival($contact); return -2; // timed out } $xml = $ret['body']; $curl_stat = $a->get_curl_code(); - if (!$curl_stat) { + if (empty($curl_stat)) { + Contact::markForArchival($contact); return -3; // timed out } logger('dfrn_deliver: ' . $xml, LOGGER_DATA); - if (! $xml) { + if (empty($xml)) { + Contact::markForArchival($contact); return 3; } if (strpos($xml, 'status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id))) { - return (($res->status) ? $res->status : 3); + if ((intval($res->status) != 0) || !strlen($res->challenge) || !strlen($res->dfrn_id)) { + Contact::markForArchival($contact); + return ($res->status ? $res->status : 3); } $postvars = []; @@ -1266,6 +1284,7 @@ class DFRN if ($final_dfrn_id != $orig_id) { logger('dfrn_deliver: wrong dfrn_id.'); // did not decode properly - cannot trust this site + Contact::markForArchival($contact); return 3; } @@ -1275,7 +1294,6 @@ class DFRN $postvars['dissolve'] = '1'; } - if ((($contact['rel']) && ($contact['rel'] != CONTACT_IS_SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { $postvars['data'] = $atom; $postvars['perm'] = 'rw'; @@ -1301,6 +1319,7 @@ class DFRN break; default: logger("rino: invalid requested version '$rino_remote_version'"); + Contact::markForArchival($contact); return -8; } @@ -1337,31 +1356,133 @@ class DFRN logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA); $curl_stat = $a->get_curl_code(); - if ((!$curl_stat) || (!strlen($xml))) { + if (empty($curl_stat) || empty($xml)) { + Contact::markForArchival($contact); return -9; // timed out } - if (($curl_stat == 503) && (stristr($a->get_curl_headers(), 'retry-after'))) { + if (($curl_stat == 503) && stristr($a->get_curl_headers(), 'retry-after')) { + Contact::markForArchival($contact); return -10; } if (strpos($xml, 'status)) { + Contact::markForArchival($contact); return -11; } + // Possibly old servers had returned an empty value when everything was okay + if (empty($res->status)) { + $res->status = 200; + } + if (!empty($res->message)) { logger('Delivery returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG); } - if ($res->status == 200) { + if (($res->status >= 200) && ($res->status <= 299)) { + Contact::unmarkForArchival($contact); + } + + return intval($res->status); + } + + /** + * @brief Transmits atom content to the contacts via the Diaspora transport layer + * + * @param array $owner Owner record + * @param array $contact Contact record of the receiver + * @param string $atom Content that will be transmitted + * + * @return int Deliver status. Negative values mean an error. + */ + public static function transmit($owner, $contact, $atom, $public_batch = false) + { + $a = get_app(); + + if (!$public_batch) { + if (empty($contact['addr'])) { + logger('Empty contact handle for ' . $contact['id'] . ' - ' . $contact['url'] . ' - trying to update it.'); + if (Contact::updateFromProbe($contact['id'])) { + $new_contact = dba::selectFirst('contact', ['addr'], ['id' => $contact['id']]); + $contact['addr'] = $new_contact['addr']; + } + + if (empty($contact['addr'])) { + logger('Unable to find contact handle for ' . $contact['id'] . ' - ' . $contact['url']); + Contact::markForArchival($contact); + return -21; + } + } + + $fcontact = Diaspora::personByHandle($contact['addr']); + if (empty($fcontact)) { + logger('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']); + Contact::markForArchival($contact); + return -22; + } + $pubkey = $fcontact['pubkey']; + } else { + $pubkey = ''; + } + + $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"]); + $path_parts = explode('/', $parts['path']); + array_pop($path_parts); + $parts['path'] = implode('/', $path_parts); + $contact["batch"] = Network::unparseURL($parts); + } + + $dest_url = ($public_batch ? $contact["batch"] : $contact["notify"]); + + $content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json"); + + $xml = Network::post($dest_url, $envelope, ["Content-Type: ".$content_type]); + + $curl_stat = $a->get_curl_code(); + if (empty($curl_stat) || empty($xml)) { + logger('Empty answer from ' . $contact['id'] . ' - ' . $dest_url); + Contact::markForArchival($contact); + return -9; // timed out + } + + if (($curl_stat == 503) && (stristr($a->get_curl_headers(), 'retry-after'))) { + Contact::markForArchival($contact); + return -10; + } + + if (strpos($xml, 'status)) { + Contact::markForArchival($contact); + return -23; + } + + if (!empty($res->message)) { + logger('Transmit to ' . $dest_url . ' returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG); + } + + if (($res->status >= 200) && ($res->status <= 299)) { Contact::unmarkForArchival($contact); } @@ -1381,33 +1502,33 @@ class DFRN // Check for duplicates $r = q( "SELECT `id` FROM `event` WHERE `uid` = %d AND `cid` = %d AND `start` = '%s' AND `type` = '%s' LIMIT 1", - intval($contact["uid"]), - intval($contact["id"]), + intval($contact['uid']), + intval($contact['id']), dbesc(DateTimeFormat::utc($birthday)), - dbesc("birthday") + dbesc('birthday') ); if (DBM::is_result($r)) { return; } - logger("updating birthday: ".$birthday." for contact ".$contact["id"]); + logger('updating birthday: ' . $birthday . ' for contact ' . $contact['id']); - $bdtext = L10n::t("%s\'s birthday", $contact["name"]); - $bdtext2 = L10n::t("Happy Birthday %s", " [url=".$contact["url"]."]".$contact["name"]."[/url]"); + $bdtext = L10n::t('%s\'s birthday', $contact['name']); + $bdtext2 = L10n::t('Happy Birthday %s', ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'); $r = q( "INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", - intval($contact["uid"]), - intval($contact["id"]), + intval($contact['uid']), + intval($contact['id']), dbesc(DateTimeFormat::utcNow()), dbesc(DateTimeFormat::utcNow()), dbesc(DateTimeFormat::utc($birthday)), - dbesc(DateTimeFormat::utc($birthday . " + 1 day ")), + dbesc(DateTimeFormat::utc($birthday . ' + 1 day ')), dbesc($bdtext), dbesc($bdtext2), - dbesc("birthday") + dbesc('birthday') ); } @@ -1433,7 +1554,7 @@ class DFRN $contact_old = dba::fetch_first("SELECT `id`, `uid`, `url`, `network`, `avatar-date`, `avatar`, `name-date`, `uri-date`, `addr`, `name`, `nick`, `about`, `location`, `keywords`, `xmpp`, `bdyear`, `bd`, `hidden`, `contact-type` FROM `contact` WHERE `uid` = ? AND `nurl` = ? AND `network` != ?", - $importer["uid"], + $importer["importer_uid"], normalise_link($author["link"]), NETWORK_STATUSNET ); @@ -1443,7 +1564,7 @@ class DFRN $author["network"] = $contact_old["network"]; } else { if (!$onlyfetch) { - logger("Contact ".$author["link"]." wasn't found for user ".$importer["uid"]." XML: ".$xml, LOGGER_DEBUG); + logger("Contact ".$author["link"]." wasn't found for user ".$importer["importer_uid"]." XML: ".$xml, LOGGER_DEBUG); } $author["contact-id"] = $importer["id"]; @@ -1459,7 +1580,7 @@ class DFRN $href = ""; $width = 0; foreach ($avatar->attributes as $attributes) { - /// @TODO Rewrite these similar if () to one switch + /// @TODO Rewrite these similar if() to one switch if ($attributes->name == "href") { $href = $attributes->textContent; } @@ -1639,7 +1760,7 @@ class DFRN Contact::updateAvatar( $author['avatar'], - $importer['uid'], + $importer['importer_uid'], $contact['id'], (strtotime($contact['avatar-date']) > strtotime($contact_old['avatar-date']) || ($author['avatar'] != $contact_old['avatar'])) ); @@ -1657,7 +1778,7 @@ class DFRN $poco["contact-type"] = $contact["contact-type"]; $gcid = GContact::update($poco); - GContact::link($gcid, $importer["uid"], $contact["id"]); + GContact::link($gcid, $importer["importer_uid"], $contact["id"]); } return $author; @@ -1961,94 +2082,31 @@ class DFRN // Update the gcontact entry $relocate["server_url"] = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $relocate["url"]); - $x = q( - "UPDATE `gcontact` SET - `name` = '%s', - `photo` = '%s', - `url` = '%s', - `nurl` = '%s', - `addr` = '%s', - `connect` = '%s', - `notify` = '%s', - `server_url` = '%s' - WHERE `nurl` = '%s';", - dbesc($relocate["name"]), - dbesc($relocate["avatar"]), - dbesc($relocate["url"]), - dbesc(normalise_link($relocate["url"])), - dbesc($relocate["addr"]), - dbesc($relocate["addr"]), - dbesc($relocate["notify"]), - dbesc($relocate["server_url"]), - dbesc(normalise_link($old["url"])) - ); + $fields = ['name' => $relocate["name"], 'photo' => $relocate["avatar"], + 'url' => $relocate["url"], 'nurl' => normalise_link($relocate["url"]), + 'addr' => $relocate["addr"], 'connect' => $relocate["addr"], + 'notify' => $relocate["notify"], 'server_url' => $relocate["server_url"]]; + dba::update('gcontact', $fields, ['nurl' => normalise_link($old["url"])]); // Update the contact table. We try to find every entry. - $x = q( - "UPDATE `contact` SET - `name` = '%s', - `avatar` = '%s', - `url` = '%s', - `nurl` = '%s', - `addr` = '%s', - `request` = '%s', - `confirm` = '%s', - `notify` = '%s', - `poll` = '%s', - `site-pubkey` = '%s' - WHERE (`id` = %d AND `uid` = %d) OR (`nurl` = '%s');", - dbesc($relocate["name"]), - dbesc($relocate["avatar"]), - dbesc($relocate["url"]), - dbesc(normalise_link($relocate["url"])), - dbesc($relocate["addr"]), - dbesc($relocate["request"]), - dbesc($relocate["confirm"]), - dbesc($relocate["notify"]), - dbesc($relocate["poll"]), - dbesc($relocate["sitepubkey"]), - intval($importer["id"]), - intval($importer["importer_uid"]), - dbesc(normalise_link($old["url"])) - ); + $fields = ['name' => $relocate["name"], 'avatar' => $relocate["avatar"], + 'url' => $relocate["url"], 'nurl' => normalise_link($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"], normalise_link($old["url"])]; + dba::update('contact', $fields, $condition); Contact::updateAvatar($relocate["avatar"], $importer["importer_uid"], $importer["id"], true); - if ($x === false) { - return false; - } + logger('Contacts are updated.'); // update items - /// @todo This is an extreme performance killer - $fields = [ - 'owner-link' => [$old["url"], $relocate["url"]], - 'author-link' => [$old["url"], $relocate["url"]], - //'owner-avatar' => array($old["photo"], $relocate["photo"]), - //'author-avatar' => array($old["photo"], $relocate["photo"]), - ]; - foreach ($fields as $n => $f) { - $r = q( - "SELECT `id` FROM `item` WHERE `%s` = '%s' AND `uid` = %d LIMIT 1", - $n, - dbesc($f[0]), - intval($importer["importer_uid"]) - ); - - if (DBM::is_result($r)) { - $x = q( - "UPDATE `item` SET `%s` = '%s' WHERE `%s` = '%s' AND `uid` = %d", - $n, - dbesc($f[1]), - $n, - dbesc($f[0]), - intval($importer["importer_uid"]) - ); + // This is an extreme performance killer + Item::update(['owner-link' => $relocate["url"]], ['owner-link' => $old["url"], 'uid' => $importer["importer_uid"]]); + Item::update(['author-link' => $relocate["url"]], ['author-link' => $old["url"], 'uid' => $importer["importer_uid"]]); - if ($x === false) { - return false; - } - } - } + logger('Items are updated.'); /// @TODO /// merge with current record, current contents have priority @@ -2617,7 +2675,7 @@ class DFRN if ((x($ev, "desc") || x($ev, "summary")) && x($ev, "start")) { logger("Event in item ".$item["uri"]." was found.", LOGGER_DEBUG); $ev["cid"] = $importer["id"]; - $ev["uid"] = $importer["uid"]; + $ev["uid"] = $importer["importer_uid"]; $ev["uri"] = $item["uri"]; $ev["edited"] = $item["edited"]; $ev["private"] = $item["private"]; @@ -2626,7 +2684,7 @@ class DFRN $r = q( "SELECT `id` FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item["uri"]), - intval($importer["uid"]) + intval($importer["importer_uid"]) ); if (DBM::is_result($r)) { $ev["id"] = $r[0]["id"]; @@ -2661,6 +2719,10 @@ class DFRN if ($posted_id) { logger("Reply from contact ".$item["contact-id"]." was stored with id ".$posted_id, LOGGER_DEBUG); + if ($item['uid'] == 0) { + Item::distribute($posted_id); + } + $item["id"] = $posted_id; $r = q( @@ -2681,6 +2743,10 @@ class DFRN return true; } } else { // $entrytype == DFRN_TOP_LEVEL + if (($importer["uid"] == 0) && ($importer["importer_uid"] != 0)) { + logger("Contact ".$importer["id"]." isn't known to user ".$importer["importer_uid"].". The post will be ignored.", LOGGER_DEBUG); + return; + } 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, @@ -2705,8 +2771,16 @@ class DFRN $posted_id = Item::insert($item, false, $notify); + if ($notify) { + $posted_id = $notify; + } + logger("Item was stored with id ".$posted_id, LOGGER_DEBUG); + if ($item['uid'] == 0) { + Item::distribute($posted_id); + } + if (stristr($item["verb"], ACTIVITY_POKE)) { self::doPoke($item, $importer, $posted_id); } @@ -2726,6 +2800,7 @@ class DFRN { logger("Processing deletions"); $uri = null; + foreach ($deletion->attributes as $attributes) { if ($attributes->name == "ref") { $uri = $attributes->textContent; @@ -2736,10 +2811,10 @@ class DFRN return false; } - $condition = ["`uri` = ? AND `uid` = ? AND NOT `file` LIKE '%[%'", $uri, $importer["uid"]]; + $condition = ["`uri` = ? AND `uid` = ? AND NOT `file` LIKE '%[%'", $uri, $importer["importer_uid"]]; $item = dba::selectFirst('item', ['id', 'parent', 'contact-id'], $condition); if (!DBM::is_result($item)) { - logger("Item with uri " . $uri . " for user " . $importer["uid"] . " wasn't found.", LOGGER_DEBUG); + logger("Item with uri " . $uri . " for user " . $importer["importer_uid"] . " wasn't found.", LOGGER_DEBUG); return; } @@ -2808,7 +2883,7 @@ class DFRN $xpath->registerNamespace("statusnet", NAMESPACE_STATUSNET); $header = []; - $header["uid"] = $importer["uid"]; + $header["uid"] = $importer["importer_uid"]; $header["network"] = NETWORK_DFRN; $header["type"] = "remote"; $header["wall"] = 0; @@ -2827,7 +2902,10 @@ class DFRN self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false, $xml); } - logger("Import DFRN message for user " . $importer["uid"] . " from contact " . $importer["id"], LOGGER_DEBUG); + logger("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"], LOGGER_DEBUG); + + // is it a public forum? Private forums aren't exposed with this method + $forum = intval($xpath->evaluate("/atom:feed/dfrn:community/text()")->item(0)->nodeValue); // The account type is new since 3.5.1 if ($xpath->query("/atom:feed/dfrn:account_type")->length > 0) { @@ -2836,38 +2914,33 @@ class DFRN if ($accounttype != $importer["contact-type"]) { dba::update('contact', ['contact-type' => $accounttype], ['id' => $importer["id"]]); } - } - - // is it a public forum? Private forums aren't supported with this method - // This is deprecated since 3.5.1 - $forum = intval($xpath->evaluate("/atom:feed/dfrn:community/text()")->item(0)->nodeValue); - - if ($forum != $importer["forum"]) { + // A forum contact can either have set "forum" or "prv" - but not both + if (($accounttype == ACCOUNT_TYPE_COMMUNITY) && (($forum != $importer["forum"]) || ($forum == $importer["prv"]))) { + $condition = ['(`forum` != ? OR `prv` != ?) AND `id` = ?', $forum, !$forum, $importer["id"]]; + dba::update('contact', ['forum' => $forum, 'prv' => !$forum], $condition); + } + } elseif ($forum != $importer["forum"]) { // Deprecated since 3.5.1 $condition = ['`forum` != ? AND `id` = ?', $forum, $importer["id"]]; dba::update('contact', ['forum' => $forum], $condition); } + // We are processing relocations even if we are ignoring a contact $relocations = $xpath->query("/atom:feed/dfrn:relocate"); foreach ($relocations as $relocation) { self::processRelocation($xpath, $relocation, $importer); } - if ($importer["readonly"]) { - // We aren't receiving stuff from this person. But we will quietly ignore them - // rather than a blatant "go away" message. - logger('ignoring contact '.$importer["id"]); - return 403; - } - - $mails = $xpath->query("/atom:feed/dfrn:mail"); - foreach ($mails as $mail) { - self::processMail($xpath, $mail, $importer); - } + 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"); - foreach ($suggestions as $suggestion) { - self::processSuggestion($xpath, $suggestion, $importer); + $suggestions = $xpath->query("/atom:feed/dfrn:suggest"); + foreach ($suggestions as $suggestion) { + self::processSuggestion($xpath, $suggestion, $importer); + } } $deletions = $xpath->query("/atom:feed/at:deleted-entry"); @@ -2895,7 +2968,7 @@ class DFRN self::processEntry($header, $xpath, $entry, $importer, $xml); } } - logger("Import done for user " . $importer["uid"] . " from contact " . $importer["id"], LOGGER_DEBUG); + logger("Import done for user " . $importer["importer_uid"] . " from contact " . $importer["id"], LOGGER_DEBUG); return 200; }