function diaspora_like($importer, $xml, $msg) { $a = get_app(); $guid = notags(unxmlify($xml->guid)); $parent_guid = notags(unxmlify($xml->parent_guid)); $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); $target_type = notags(unxmlify($xml->target_type)); $positive = notags(unxmlify($xml->positive)); $author_signature = notags(unxmlify($xml->author_signature)); $parent_author_signature = $xml->parent_author_signature ? notags(unxmlify($xml->parent_author_signature)) : ''; // likes on comments not supported here and likes on photos not supported by Diaspora // if($target_type !== 'Post') // return; $contact = diaspora_get_contact_by_handle($importer['uid'], $msg['author']); if (!$contact) { logger('diaspora_like: cannot find contact: ' . $msg['author']); return; } if (!diaspora_post_allow($importer, $contact, false)) { logger('diaspora_like: Ignoring this author.'); return 202; } $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", intval($importer['uid']), dbesc($parent_guid)); if (!count($r)) { $result = diaspora_store_by_guid($parent_guid, $contact['url'], $importer['uid']); if (!$result) { $person = find_diaspora_person_by_handle($diaspora_handle); $result = diaspora_store_by_guid($parent_guid, $person['url'], $importer['uid']); } if ($result) { logger("Fetched missing item " . $parent_guid . " - result: " . $result, LOGGER_DEBUG); $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", intval($importer['uid']), dbesc($parent_guid)); } } if (!count($r)) { logger('diaspora_like: parent item not found: ' . $guid); return; } $parent_item = $r[0]; $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", intval($importer['uid']), dbesc($guid)); if (count($r)) { if ($positive === 'true') { logger('diaspora_like: duplicate like: ' . $guid); return; } // Note: I don't think "Like" objects with positive = "false" are ever actually used // It looks like "RelayableRetractions" are used for "unlike" instead if ($positive === 'false') { logger('diaspora_like: received a like with positive set to "false"...ignoring'); /* q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d", intval($r[0]['id']), intval($importer['uid']) );*/ // FIXME--actually don't unless it turns out that Diaspora does indeed send out "false" likes // send notification via proc_run() return; } } // Note: I don't think "Like" objects with positive = "false" are ever actually used // It looks like "RelayableRetractions" are used for "unlike" instead if ($positive === 'false') { logger('diaspora_like: received a like with positive set to "false"'); logger('diaspora_like: unlike received with no corresponding like...ignoring'); return; } /* How Diaspora performs "like" signature checking: - If an item has been sent by the like author to the top-level post owner to relay on to the rest of the contacts on the top-level post, the top-level post owner should check the author_signature, then create a parent_author_signature before relaying the like on - If an item has been relayed on by the top-level post owner, the contacts who receive it check only the parent_author_signature. Basically, they trust that the top-level post owner has already verified the authenticity of anything he/she sends out - In either case, the signature that get checked is the signature created by the person who sent the salmon */ // Diaspora has changed the way they are signing the likes. // Just to make sure that we don't miss any likes we will check the old and the current way. $old_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle; $signed_data = $positive . ';' . $guid . ';' . $target_type . ';' . $parent_guid . ';' . $diaspora_handle; $key = $msg['key']; if ($parent_author_signature) { // If a parent_author_signature exists, then we've received the like // relayed from the top-level post owner. There's no need to check the // author_signature if the parent_author_signature is valid $parent_author_signature = base64_decode($parent_author_signature); if (!rsa_verify($signed_data, $parent_author_signature, $key, 'sha256') and !rsa_verify($old_signed_data, $parent_author_signature, $key, 'sha256')) { logger('diaspora_like: top-level owner verification failed.'); return; } } else { // If there's no parent_author_signature, then we've received the like // from the like creator. In that case, the person is "like"ing // our post, so he/she must be a contact of ours and his/her public key // should be in $msg['key'] $author_signature = base64_decode($author_signature); if (!rsa_verify($signed_data, $author_signature, $key, 'sha256') and !rsa_verify($old_signed_data, $author_signature, $key, 'sha256')) { logger('diaspora_like: like creator verification failed.'); return; } } // Phew! Everything checks out. Now create an item. // Find the original comment author information. // We need this to make sure we display the comment author // information (name and avatar) correctly. if (strcasecmp($diaspora_handle, $msg['author']) == 0) { $person = $contact; } else { $person = find_diaspora_person_by_handle($diaspora_handle); if (!is_array($person)) { logger('diaspora_like: unable to find author details'); return; } } $uri = $diaspora_handle . ':' . $guid; $activity = ACTIVITY_LIKE; $post_type = $parent_item['resource-id'] ? t('photo') : t('status'); $objtype = $parent_item['resource-id'] ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE; $link = xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . '" />' . "\n"); $body = $parent_item['body']; $obj = <<<EOT \t<object> \t\t<type>{$objtype}</type> \t\t<local>1</local> \t\t<id>{$parent_item['uri']}</id> \t\t<link>{$link}</link> \t\t<title></title> \t\t<content>{$body}</content> \t</object> EOT; $bodyverb = t('%1$s likes %2$s\'s %3$s'); $arr = array(); $arr['uri'] = $uri; $arr['uid'] = $importer['uid']; $arr['guid'] = $guid; $arr['network'] = NETWORK_DIASPORA; $arr['contact-id'] = $contact['id']; $arr['type'] = 'activity'; $arr['wall'] = $parent_item['wall']; $arr['gravity'] = GRAVITY_LIKE; $arr['parent'] = $parent_item['id']; $arr['parent-uri'] = $parent_item['uri']; $arr['owner-name'] = $parent_item['name']; $arr['owner-link'] = $parent_item['url']; //$arr['owner-avatar'] = $parent_item['thumb']; $arr['owner-avatar'] = x($parent_item, 'thumb') ? $parent_item['thumb'] : $parent_item['photo']; $arr['author-name'] = $person['name']; $arr['author-link'] = $person['url']; $arr['author-avatar'] = x($person, 'thumb') ? $person['thumb'] : $person['photo']; $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'; $alink = '[url=' . $parent_item['author-link'] . ']' . $parent_item['author-name'] . '[/url]'; //$plink = '[url=' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . ']' . $post_type . '[/url]'; $plink = '[url=' . $a->get_baseurl() . '/display/' . urlencode($guid) . ']' . $post_type . '[/url]'; $arr['body'] = sprintf($bodyverb, $ulink, $alink, $plink); $arr['app'] = 'Diaspora'; $arr['private'] = $parent_item['private']; $arr['verb'] = $activity; $arr['object-type'] = $objtype; $arr['object'] = $obj; $arr['visible'] = 1; $arr['unseen'] = 1; $arr['last-child'] = 0; $message_id = item_store($arr); //if($message_id) { // q("update item set plink = '%s' where id = %d", // //dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id), // dbesc($a->get_baseurl().'/display/'.$guid), // intval($message_id) // ); //} if (!$parent_author_signature) { q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", intval($message_id), dbesc($signed_data), dbesc(base64_encode($author_signature)), dbesc($diaspora_handle)); } // if the message isn't already being relayed, notify others // the existence of parent_author_signature means the parent_author or owner // is already relaying. The parent_item['origin'] indicates the message was created on our system if ($parent_item['origin'] && !$parent_author_signature) { proc_run('php', 'include/notifier.php', 'comment-import', $message_id); } return; }
function diaspora_like($importer, $xml, $msg) { $a = get_app(); $guid = notags(unxmlify($xml->guid)); $parent_guid = notags(unxmlify($xml->parent_guid)); $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); $target_type = notags(unxmlify($xml->target_type)); $positive = notags(unxmlify($xml->positive)); $author_signature = notags(unxmlify($xml->author_signature)); $parent_author_signature = $xml->parent_author_signature ? notags(unxmlify($xml->parent_author_signature)) : ''; // likes on comments not supported here and likes on photos not supported by Diaspora if ($target_type !== 'Post') { return; } $contact = diaspora_get_contact_by_handle($importer['uid'], $msg['author']); if (!$contact) { logger('diaspora_like: cannot find contact: ' . $msg['author']); return; } if (!diaspora_post_allow($importer, $contact)) { logger('diaspora_like: Ignoring this author.'); return 202; } $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", intval($importer['uid']), dbesc($parent_guid)); if (!count($r)) { logger('diaspora_like: parent item not found: ' . $guid); return; } $parent_item = $r[0]; $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", intval($importer['uid']), dbesc($guid)); if (count($r)) { if ($positive === 'true') { logger('diaspora_like: duplicate like: ' . $guid); return; } // Note: I don't think "Like" objects with positive = "false" are ever actually used // It looks like "RelayableRetractions" are used for "unlike" instead if ($positive === 'false') { logger('diaspora_like: received a like with positive set to "false"...ignoring'); /* q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($r[0]['id']), intval($importer['uid']) );*/ // FIXME // send notification via proc_run() return; } } // Note: I don't think "Like" objects with positive = "false" are ever actually used // It looks like "RelayableRetractions" are used for "unlike" instead if ($positive === 'false') { logger('diaspora_like: received a like with positive set to "false"'); logger('diaspora_like: unlike received with no corresponding like...ignoring'); return; } $signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle; $author_signature = base64_decode($author_signature); if (strcasecmp($diaspora_handle, $msg['author']) == 0) { $person = $contact; $key = $msg['key']; } else { $person = find_diaspora_person_by_handle($diaspora_handle); if (is_array($person) && x($person, 'pubkey')) { $key = $person['pubkey']; } else { logger('diaspora_like: unable to find author details'); return; } } if (!rsa_verify($signed_data, $author_signature, $key, 'sha256')) { logger('diaspora_like: verification failed.'); return; } if ($parent_author_signature) { //$owner_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle; $parent_author_signature = base64_decode($parent_author_signature); $key = $msg['key']; if (!rsa_verify($signed_data, $parent_author_signature, $key, 'sha256')) { logger('diaspora_like: owner verification failed.'); return; } } // Phew! Everything checks out. Now create an item. $uri = $diaspora_handle . ':' . $guid; $activity = ACTIVITY_LIKE; $post_type = $parent_item['resource-id'] ? t('photo') : t('status'); $objtype = $parent_item['resource-id'] ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE; $link = xmlify('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . '" />' . "\n"); $body = $parent_item['body']; $obj = <<<EOT \t<object> \t\t<type>{$objtype}</type> \t\t<local>1</local> \t\t<id>{$parent_item['uri']}</id> \t\t<link>{$link}</link> \t\t<title></title> \t\t<content>{$body}</content> \t</object> EOT; $bodyverb = t('%1$s likes %2$s\'s %3$s'); $arr = array(); $arr['uri'] = $uri; $arr['uid'] = $importer['uid']; $arr['guid'] = $guid; $arr['contact-id'] = $contact['id']; $arr['type'] = 'activity'; $arr['wall'] = $parent_item['wall']; $arr['gravity'] = GRAVITY_LIKE; $arr['parent'] = $parent_item['id']; $arr['parent-uri'] = $parent_item['uri']; $arr['owner-name'] = $parent_item['name']; $arr['owner-link'] = $parent_item['url']; $arr['owner-avatar'] = $parent_item['thumb']; $arr['author-name'] = $person['name']; $arr['author-link'] = $person['url']; $arr['author-avatar'] = x($person, 'thumb') ? $person['thumb'] : $person['photo']; $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'; $alink = '[url=' . $parent_item['author-link'] . ']' . $parent_item['author-name'] . '[/url]'; $plink = '[url=' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . ']' . $post_type . '[/url]'; $arr['body'] = sprintf($bodyverb, $ulink, $alink, $plink); $arr['app'] = 'Diaspora'; $arr['private'] = $parent_item['private']; $arr['verb'] = $activity; $arr['object-type'] = $objtype; $arr['object'] = $obj; $arr['visible'] = 1; $arr['unseen'] = 1; $arr['last-child'] = 0; $message_id = item_store($arr); if ($message_id) { q("update item set plink = '%s' where id = %d limit 1", dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id), intval($message_id)); } if (!$parent_author_signature) { q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", intval($message_id), dbesc($author_signed_data), dbesc(base64_encode($author_signature)), dbesc($diaspora_handle)); } // if the message isn't already being relayed, notify others // the existence of parent_author_signature means the parent_author or owner // is already relaying. The parent_item['origin'] indicates the message was created on our system if ($parent_item['origin'] && !$parent_author_signature) { proc_run('php', 'include/notifier.php', 'comment', $message_id); } return; }