Beispiel #1
0
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;
}
Beispiel #2
0
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['channel_id'], $msg['author']);
    if (!$contact) {
        logger('diaspora_like: cannot find contact: ' . $msg['author']);
        return;
    }
    if (!perm_is_allowed($importer['channel_id'], $contact['xchan_hash'], 'post_comments')) {
        logger('diaspora_like: Ignoring this author.');
        return 202;
    }
    $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `mid` = '%s' LIMIT 1", intval($importer['channel_id']), 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 `mid` = '%s' LIMIT 1", intval($importer['channel_id']), 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');
            // perhaps call drop_item()
            // FIXME--actually don't unless it turns out that Diaspora does indeed send out "false" likes
            //  send notification via proc_run()
            return;
        }
    }
    $i = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($parent_item['author_xchan']));
    if ($i) {
        $item_author = $i[0];
    }
    // 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
    	*/
    // 2014-09-10 let's try this: signatures are failing. I'll try and make a signable string from
    // the parameters in the order they were presented in the post. This is how D* creates the signable string.
    $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')) {
            if (intval(get_config('system', 'ignore_diaspora_like_signature'))) {
                logger('diaspora_like: top-level owner verification failed. Proceeding anyway.');
            } else {
                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')) {
            if (intval(get_config('system', 'ignore_diaspora_like_signature'))) {
                logger('diaspora_like: like creator verification failed. Proceeding anyway');
            } else {
                logger('diaspora_like: like creator verification failed.');
                return;
            }
        }
    }
    logger('diaspora_like: signature check complete.', LOGGER_DEBUG);
    // 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_type'] === 'photo' ? t('photo') : t('status');
    $links = array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $parent_item['plink']));
    $objtype = $parent_item['resource_type'] === 'photo' ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE;
    $body = $parent_item['body'];
    $object = json_encode(array('type' => $post_type, 'id' => $parent_item['mid'], 'parent' => $parent_item['thr_parent'] ? $parent_item['thr_parent'] : $parent_item['parent_mid'], 'link' => $links, 'title' => $parent_item['title'], 'content' => $parent_item['body'], 'created' => $parent_item['created'], 'edited' => $parent_item['edited'], 'author' => array('name' => $item_author['xchan_name'], 'address' => $item_author['xchan_addr'], 'guid' => $item_author['xchan_guid'], 'guid_sig' => $item_author['xchan_guid_sig'], 'link' => array(array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']), array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m'])))));
    $bodyverb = t('%1$s likes %2$s\'s %3$s');
    $arr = array();
    $arr['uid'] = $importer['channel_id'];
    $arr['aid'] = $importer['channel_account_id'];
    $arr['mid'] = $guid;
    $arr['parent_mid'] = $parent_item['mid'];
    $arr['owner_xchan'] = $parent_item['owner_xchan'];
    $arr['author_xchan'] = $person['xchan_hash'];
    $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]';
    $alink = '[url=' . $parent_item['author-link'] . ']' . $parent_item['author-name'] . '[/url]';
    $plink = '[url=' . z_root() . '/display/' . $guid . ']' . $post_type . '[/url]';
    $arr['body'] = sprintf($bodyverb, $ulink, $alink, $plink);
    $arr['app'] = 'Diaspora';
    $arr['item_private'] = $parent_item['item_private'];
    $arr['verb'] = $activity;
    $arr['object-type'] = $objtype;
    $arr['object'] = $object;
    if (!$parent_author_signature) {
        $datarray['diaspora_meta'] = array('signer' => $diaspora_handle, 'body' => $text, 'signed_text' => $signed_data, 'signature' => base64_encode($author_signature));
    }
    $x = item_store($arr);
    if ($x) {
        $message_id = $x['item_id'];
    }
    // 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['item_flags'] & ITEM_ORIGIN && !$parent_author_signature) {
        proc_run('php', 'include/notifier.php', 'comment-import', $message_id);
    }
    return;
}
Beispiel #3
0
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 ($contact['rel'] == CONTACT_IS_FOLLOWER || $contact['blocked'] || $contact['readonly']) {
        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;
        }
        if ($positive === 'false') {
            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;
        }
    }
    if ($positive === 'false') {
        logger('diaspora_like: unlike received with no corresponding like');
        return;
    }
    $author_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($author_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($owner_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;
}
Beispiel #4
0
function get_diaspora_key($handle)
{
    logger('Fetching diaspora key for: ' . $handle, LOGGER_DEBUG);
    $r = find_diaspora_person_by_handle($handle);
    return $r ? $r['xchan_pubkey'] : '';
}
Beispiel #5
0
function diaspora_signed_retraction($importer, $xml, $msg)
{
    $guid = notags(unxmlify($xml->target_guid));
    $diaspora_handle = notags(unxmlify($xml->sender_handle));
    $type = notags(unxmlify($xml->target_type));
    $sig = notags(unxmlify($xml->target_author_signature));
    $parent_author_signature = $xml->parent_author_signature ? notags(unxmlify($xml->parent_author_signature)) : '';
    $contact = diaspora_get_contact_by_handle($importer['uid'], $diaspora_handle);
    if (!$contact) {
        logger('diaspora_signed_retraction: no contact');
        return;
    }
    $signed_data = $guid . ';' . $type;
    $sig_decode = base64_decode($sig);
    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_signed_retraction: unable to find author details');
            return;
        }
    }
    if (!rsa_verify($signed_data, $sig_decode, $key, 'sha256')) {
        logger('diaspora_signed_retraction: retraction-owner verification failed.' . print_r($msg, true));
        return;
    }
    if ($parent_author_signature) {
        $parent_author_signature = base64_decode($parent_author_signature);
        $key = $msg['key'];
        if (!rsa_verify($signed_data, $parent_author_signature, $key, 'sha256')) {
            logger('diaspora_signed_retraction: failed to verify person relaying the retraction (e.g. owner of a post relaying a retracted comment');
            return;
        }
    }
    if ($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
        $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1", dbesc($guid), intval($importer['uid']));
        if (count($r)) {
            if (link_compare($r[0]['author-link'], $contact['url'])) {
                q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d limit 1", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($r[0]['id']));
                // Now check if the retraction needs to be relayed by us
                //
                // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
                // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
                // The only item with `parent` and `id` as the parent id is the parent item.
                $p = q("select origin from item where parent = %d and id = %d limit 1", $r[0]['parent'], $r[0]['parent']);
                if (count($p)) {
                    if ($p[0]['origin'] && !$parent_author_signature) {
                        q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", $r[0]['id'], dbesc($signed_data), dbesc($sig), dbesc($diaspora_handle));
                        // the existence of parent_author_signature would have meant the parent_author or owner
                        // is already relaying.
                        logger('diaspora_signed_retraction: relaying relayable_retraction');
                        proc_run('php', 'include/notifier.php', 'relayable_retraction', $r[0]['id']);
                    }
                }
            }
        }
    } else {
        logger('diaspora_signed_retraction: unknown type: ' . $type);
    }
    return 202;
    // NOTREACHED
}