Merge pull request #323 from fermionic/add-relayable-retraction-support-for-diaspora

Add relayable retraction support for diaspora
This commit is contained in:
friendica 2012-06-03 14:47:04 -07:00
commit a66baf2918
12 changed files with 371 additions and 77 deletions

View File

@ -11,7 +11,7 @@ require_once('include/cache.php');
define ( 'FRIENDICA_PLATFORM', 'Friendica'); define ( 'FRIENDICA_PLATFORM', 'Friendica');
define ( 'FRIENDICA_VERSION', '3.0.1362' ); define ( 'FRIENDICA_VERSION', '3.0.1362' );
define ( 'DFRN_PROTOCOL_VERSION', '2.23' ); define ( 'DFRN_PROTOCOL_VERSION', '2.23' );
define ( 'DB_UPDATE_VERSION', 1147 ); define ( 'DB_UPDATE_VERSION', 1148 );
define ( 'EOL', "<br />\r\n" ); define ( 'EOL', "<br />\r\n" );
define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' ); define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );

View File

@ -944,12 +944,14 @@ CREATE TABLE IF NOT EXISTS `session` (
CREATE TABLE IF NOT EXISTS `sign` ( CREATE TABLE IF NOT EXISTS `sign` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`iid` int(10) unsigned NOT NULL, `iid` int(10) unsigned NOT NULL DEFAULT '0',
`retract_iid` int(10) unsigned NOT NULL DEFAULT '0',
`signed_text` mediumtext NOT NULL, `signed_text` mediumtext NOT NULL,
`signature` text NOT NULL, `signature` text NOT NULL,
`signer` char(255) NOT NULL, `signer` char(255) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `iid` (`iid`) KEY `iid` (`iid`),
KEY `retract_iid` (`retract_iid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- -------------------------------------------------------- -- --------------------------------------------------------

View File

@ -113,6 +113,7 @@ function delivery_run($argv, $argc){
$uid = $r[0]['uid']; $uid = $r[0]['uid'];
$updated = $r[0]['edited']; $updated = $r[0]['edited'];
// The following seems superfluous. We've already checked for "if (! intval($r[0]['parent']))" a few lines up
if(! $parent_id) if(! $parent_id)
continue; continue;
@ -508,17 +509,17 @@ function delivery_run($argv, $argc){
// unsupported // unsupported
break; break;
} }
elseif(($target_item['deleted']) && ($target_item['verb'] !== ACTIVITY_LIKE)) { elseif(($target_item['deleted']) && ($target_item['uri'] === $target_item['parent-uri'])) {
logger('delivery: diaspora retract: ' . $loc); // top-level retraction
// diaspora delete, logger('delivery: diaspora retract: ' . $loc);
diaspora_send_retraction($target_item,$owner,$contact,$public_message); diaspora_send_retraction($target_item,$owner,$contact,$public_message);
break; break;
} }
elseif($target_item['parent'] != $target_item['id']) { elseif($target_item['uri'] !== $target_item['parent-uri']) {
// we are the relay - send comments, likes and relayable_retractions to our conversants
logger('delivery: diaspora relay: ' . $loc);
logger('delivery: diaspora relay: ' . $loc);
// we are the relay - send comments, likes and unlikes to our conversants
diaspora_send_relay($target_item,$owner,$contact,$public_message); diaspora_send_relay($target_item,$owner,$contact,$public_message);
break; break;
} }

193
include/diaspora.php Normal file → Executable file
View File

@ -83,6 +83,9 @@ function diaspora_dispatch($importer,$msg) {
elseif($xmlbase->signed_retraction) { elseif($xmlbase->signed_retraction) {
$ret = diaspora_signed_retraction($importer,$xmlbase->signed_retraction,$msg); $ret = diaspora_signed_retraction($importer,$xmlbase->signed_retraction,$msg);
} }
elseif($xmlbase->relayable_retraction) {
$ret = diaspora_signed_retraction($importer,$xmlbase->relayable_retraction,$msg);
}
elseif($xmlbase->photo) { elseif($xmlbase->photo) {
$ret = diaspora_photo($importer,$xmlbase->photo,$msg); $ret = diaspora_photo($importer,$xmlbase->photo,$msg);
} }
@ -677,7 +680,7 @@ function diaspora_post($importer,$xml) {
return; return;
} }
// allocate a guid on our system - we aren't fixing any collisions. // allocate a guid on our system - we aren't fixing any collisions.
// we're ignoring them // we're ignoring them
$g = q("select * from guid where guid = '%s' limit 1", $g = q("select * from guid where guid = '%s' limit 1",
@ -844,7 +847,7 @@ function diaspora_reshare($importer,$xml) {
$prefix = '&#x2672; ' . $details . "\n"; $prefix = '&#x2672; ' . $details . "\n";
// allocate a guid on our system - we aren't fixing any collisions. // allocate a guid on our system - we aren't fixing any collisions.
// we're ignoring them // we're ignoring them
$g = q("select * from guid where guid = '%s' limit 1", $g = q("select * from guid where guid = '%s' limit 1",
@ -948,7 +951,7 @@ function diaspora_asphoto($importer,$xml) {
return; return;
} }
// allocate a guid on our system - we aren't fixing any collisions. // allocate a guid on our system - we aren't fixing any collisions.
// we're ignoring them // we're ignoring them
$g = q("select * from guid where guid = '%s' limit 1", $g = q("select * from guid where guid = '%s' limit 1",
@ -1602,22 +1605,28 @@ function diaspora_like($importer,$xml,$msg) {
logger('diaspora_like: duplicate like: ' . $guid); logger('diaspora_like: duplicate like: ' . $guid);
return; 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') { if($positive === 'false') {
q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d LIMIT 1", 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($r[0]['id']),
intval($importer['uid']) intval($importer['uid'])
); );*/
// FIXME // FIXME
// send notification via proc_run() // send notification via proc_run()
return; 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') { if($positive === 'false') {
logger('diaspora_like: unlike received with no corresponding like'); logger('diaspora_like: received a like with positive set to "false"');
logger('diaspora_like: unlike received with no corresponding like...ignoring');
return; return;
} }
$author_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle; $signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
$author_signature = base64_decode($author_signature); $author_signature = base64_decode($author_signature);
@ -1635,20 +1644,20 @@ function diaspora_like($importer,$xml,$msg) {
} }
} }
if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) { if(! rsa_verify($signed_data,$author_signature,$key,'sha256')) {
logger('diaspora_like: verification failed.'); logger('diaspora_like: verification failed.');
return; return;
} }
if($parent_author_signature) { if($parent_author_signature) {
$owner_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle; //$owner_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
$parent_author_signature = base64_decode($parent_author_signature); $parent_author_signature = base64_decode($parent_author_signature);
$key = $msg['key']; $key = $msg['key'];
if(! rsa_verify($owner_signed_data,$parent_author_signature,$key,'sha256')) { if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
logger('diaspora_like: owner verification failed.'); logger('diaspora_like: owner verification failed.');
return; return;
} }
@ -1783,38 +1792,89 @@ function diaspora_signed_retraction($importer,$xml,$msg) {
$type = notags(unxmlify($xml->target_type)); $type = notags(unxmlify($xml->target_type));
$sig = notags(unxmlify($xml->target_author_signature)); $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); $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
if(! $contact) { if(! $contact) {
logger('diaspora_signed_retraction: no contact'); logger('diaspora_signed_retraction: no contact');
return; return;
} }
// this may not yet work for comments. Need to see how the relaying works
// and figure out who signs it.
$signed_data = $guid . ';' . $type ; $signed_data = $guid . ';' . $type ;
$sig = base64_decode($sig); $sig_decode = base64_decode($sig);
$key = $msg['key']; if(strcasecmp($diaspora_handle,$msg['author']) == 0) {
$person = $contact;
$key = $msg['key'];
}
else {
$person = find_diaspora_person_by_handle($diaspora_handle);
if(! rsa_verify($signed_data,$sig,$key,'sha256')) { if(is_array($person) && x($person,'pubkey'))
logger('diaspora_signed_retraction: owner verification failed.' . print_r($msg,true)); $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; return;
} }
if($type === 'StatusMessage') { 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", $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1",
dbesc($guid), dbesc($guid),
intval($importer['uid']) intval($importer['uid'])
); );
if(count($r)) { if(count($r)) {
if(link_compare($r[0]['author-link'],$contact['url'])) { if(link_compare($r[0]['author-link'],$contact['url'])) {
q("update item set `deleted` = 1, `changed` = '%s' where `id` = %d limit 1", q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d limit 1",
dbesc(datetime_convert()),
dbesc(datetime_convert()), dbesc(datetime_convert()),
intval($r[0]['id']) 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']);
}
}
} }
} }
} }
@ -2071,7 +2131,11 @@ function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
$tpl = get_markup_template('diaspora_like.tpl'); $tpl = get_markup_template('diaspora_like.tpl');
$like = true; $like = true;
$target_type = 'Post'; $target_type = 'Post';
$positive = (($item['deleted']) ? 'false' : 'true'); // $positive = (($item['deleted']) ? 'false' : 'true');
$positive = 'true';
if(($item['deleted']))
logger('diaspora_send_followup: received deleted "like". Those should go to diaspora_send_retraction');
} }
else { else {
$tpl = get_markup_template('diaspora_comment.tpl'); $tpl = get_markup_template('diaspora_comment.tpl');
@ -2111,7 +2175,7 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
$a = get_app(); $a = get_app();
$myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
$theiraddr = $contact['addr']; $theiraddr = $contact['addr'];
@ -2127,29 +2191,35 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
else else
return; return;
if($item['verb'] === ACTIVITY_LIKE) { $like = false;
$relay_retract = false;
$sql_sign_id = 'iid';
if( $item['deleted']) {
$tpl = get_markup_template('diaspora_relayable_retraction.tpl');
$relay_retract = true;
$sql_sign_id = 'retract_iid';
$target_type = ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
}
elseif($item['verb'] === ACTIVITY_LIKE) {
$tpl = get_markup_template('diaspora_like_relay.tpl'); $tpl = get_markup_template('diaspora_like_relay.tpl');
$like = true; $like = true;
$target_type = 'Post'; $target_type = 'Post';
$positive = (($item['deleted']) ? 'false' : 'true'); // $positive = (($item['deleted']) ? 'false' : 'true');
$positive = 'true';
} }
else { else {
$tpl = get_markup_template('diaspora_comment_relay.tpl'); $tpl = get_markup_template('diaspora_comment_relay.tpl');
$like = false;
} }
$body = $item['body']; $body = $item['body'];
$text = html_entity_decode(bb2diaspora($body)); $text = html_entity_decode(bb2diaspora($body));
// fetch the original signature if somebody sent the post to us to relay
// If we are relaying for a reply originating on our own account, there wasn't a 'send to relay'
// action. It wasn't needed. In that case create the original signature and the
// owner (parent author) signature
// comments from other networks will be relayed under our name, with a brief
// preamble to describe what's happening and noting the real author
$r = q("select * from sign where iid = %d limit 1", // fetch the original signature if the relayable was created by a Diaspora
// or DFRN user. Relayables for other networks are not supported.
$r = q("select * from sign where " . $sql_sign_id . " = %d limit 1",
intval($item['id']) intval($item['id'])
); );
if(count($r)) { if(count($r)) {
@ -2160,6 +2230,12 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
} }
else { else {
// Author signature information (for likes, comments, and retractions of likes or comments,
// whether from Diaspora or Friendica) must be placed in the `sign` table before this
// function is called
logger('diaspora_send_relay: original author signature not found, cannot send relayable');
return;
/*
$itemcontact = q("select * from contact where `id` = %d limit 1", $itemcontact = q("select * from contact where `id` = %d limit 1",
intval($item['contact-id']) intval($item['contact-id'])
); );
@ -2168,29 +2244,40 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
$prefix = sprintf( t('[Relayed] Comment authored by %s from network %s'), $prefix = sprintf( t('[Relayed] Comment authored by %s from network %s'),
'['. $item['author-name'] . ']' . '(' . $item['author-link'] . ')', '['. $item['author-name'] . ']' . '(' . $item['author-link'] . ')',
network_to_name($itemcontact['network'])) . "\n"; network_to_name($itemcontact['network'])) . "\n";
// "$body" was assigned to "$text" above. It isn't used after that, so I don't think
// the following change will do anything
$body = $prefix . $body; $body = $prefix . $body;
// I think this comment will fail upon reaching Diaspora, because "$signed_text" is not defined
} }
} }
else { else {
// I'm confused about this "else." Since it sets "$handle = $myaddr," it seems like it should be for the case
// where the top-level post owner commented on his own post, i.e. "$itemcontact[0]['self']" is true. But it's
// positioned to be for the case where "count($itemcontact)" is 0.
$handle = $myaddr;
if($like) if($like)
$signed_text = $item['guid'] . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $myaddr; $signed_text = $item['guid'] . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $handle;
elseif($relay_retract)
$signed_text = $item['guid'] . ';' . $target_type;
else else
$signed_text = $item['guid'] . ';' . $parent_guid . ';' . $text . ';' . $myaddr; $signed_text = $item['guid'] . ';' . $parent_guid . ';' . $text . ';' . $handle;
$authorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')); $authorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", q("insert into sign (`" . $sql_sign_id . "`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
intval($item['id']), intval($item['id']),
dbesc($signed_text), dbesc($signed_text),
dbesc(base64_encode($authorsig)), dbesc($authorsig),
dbesc($myaddr) dbesc($handle)
); );
$handle = $myaddr;
} }
*/
} }
// sign it // sign it with the top-level owner's signature
$parentauthorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')); $parentauthorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
@ -2198,14 +2285,15 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
'$guid' => xmlify($item['guid']), '$guid' => xmlify($item['guid']),
'$parent_guid' => xmlify($parent_guid), '$parent_guid' => xmlify($parent_guid),
'$target_type' =>xmlify($target_type), '$target_type' =>xmlify($target_type),
'$authorsig' => xmlify($orig_sign['signature']), '$authorsig' => xmlify($authorsig),
'$parentsig' => xmlify($parentauthorsig), '$parentsig' => xmlify($parentauthorsig),
'$body' => xmlify($text), '$body' => xmlify($text),
'$positive' => xmlify($positive), '$positive' => xmlify($positive),
'$handle' => xmlify($handle) '$handle' => xmlify($handle)
)); ));
logger('diaspora_relay_comment: base message: ' . $msg, LOGGER_DATA); logger('diaspora_send_relay: base message: ' . $msg, LOGGER_DATA);
$slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch))); $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
@ -2220,14 +2308,25 @@ function diaspora_send_retraction($item,$owner,$contact,$public_batch = false) {
$a = get_app(); $a = get_app();
$myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
$signed_text = $item['guid'] . ';' . 'StatusMessage'; // Check whether the retraction is for a top-level post or whether it's a relayable
if( $item['uri'] !== $item['parent-uri'] ) {
$tpl = get_markup_template('diaspora_relay_retraction.tpl');
$target_type = (($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
}
else {
$tpl = get_markup_template('diaspora_signed_retract.tpl');
$target_type = 'StatusMessage';
}
$signed_text = $item['guid'] . ';' . $target_type;
$tpl = get_markup_template('diaspora_signed_retract.tpl');
$msg = replace_macros($tpl, array( $msg = replace_macros($tpl, array(
'$guid' => $item['guid'], '$guid' => xmlify($item['guid']),
'$type' => 'StatusMessage', '$type' => xmlify($target_type),
'$handle' => $myaddr, '$handle' => xmlify($myaddr),
'$signature' => base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')) '$signature' => xmlify(base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')))
)); ));
$slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch))); $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
@ -2351,3 +2450,5 @@ function diaspora_transmit($owner,$contact,$slap,$public_batch) {
return(($return_code) ? $return_code : (-1)); return(($return_code) ? $return_code : (-1));
} }

37
include/items.php Normal file → Executable file
View File

@ -3278,7 +3278,42 @@ function drop_item($id,$interactive = true) {
q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1", q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1",
intval($r[0]['id']) intval($r[0]['id'])
); );
} }
// Add a relayable_retraction signature for Diaspora. Note that we can't add a target_author_signature
// if the comment was deleted by a remote user. That should be ok, because if a remote user is deleting
// the comment, that means we're the home of the post, and Diaspora will only
// check the parent_author_signature of retractions that it doesn't have to relay further
//
// I don't think this function gets called for an "unlike," but I'll check anyway
$signed_text = $item['guid'] . ';' . ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
if(local_user() == $item['uid']) {
$handle = $a->user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
$authorsig = base64_encode(rsa_sign($signed_text,$a->user['prvkey'],'sha256'));
}
else {
$r = q("SELECT `nick`, `url` FROM `contact` WHERE `id` = '%d' LIMIT 1",
$item['contact-id']
);
if(count($r)) {
// The below handle only works for NETWORK_DFRN. I think that's ok, because this function
// only handles DFRN deletes
$handle_baseurl_start = strpos($r['url'],'://') + 3;
$handle_baseurl_length = strpos($r['url'],'/profile') - $handle_baseurl_start;
$handle = $r['nick'] . '@' . substr($r['url'], $handle_baseurl_start, $handle_baseurl_length);
$authorsig = '';
}
}
if(isset($handle))
q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
intval($item['id']),
dbesc($signed_text),
dbesc($authorsig),
dbesc($handle)
);
} }
$drop_id = intval($item['id']); $drop_id = intval($item['id']);

View File

@ -125,6 +125,7 @@ function notifier_run($argv, $argc){
$uid = $r[0]['uid']; $uid = $r[0]['uid'];
$updated = $r[0]['edited']; $updated = $r[0]['edited'];
// The following seems superfluous. We've already checked for "if (! intval($r[0]['parent']))" a few lines up
if(! $parent_id) if(! $parent_id)
return; return;
@ -596,7 +597,7 @@ function notifier_run($argv, $argc){
break; break;
case NETWORK_OSTATUS: case NETWORK_OSTATUS:
// Do not send to otatus if we are not configured to send to public networks // Do not send to ostatus if we are not configured to send to public networks
if($owner['prvnets']) if($owner['prvnets'])
break; break;
if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only')) if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only'))
@ -737,18 +738,19 @@ function notifier_run($argv, $argc){
// unsupported // unsupported
break; break;
} }
elseif(($target_item['deleted']) && ($target_item['verb'] !== ACTIVITY_LIKE)) { elseif(($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
// diaspora delete, // send both top-level retractions and relayable retractions for owner to relay
diaspora_send_retraction($target_item,$owner,$contact); diaspora_send_retraction($target_item,$owner,$contact);
break; break;
} }
elseif($followup) { elseif($followup) {
// send comments, likes and retractions of likes to owner to relay // send comments and likes to owner to relay
diaspora_send_followup($target_item,$owner,$contact); diaspora_send_followup($target_item,$owner,$contact);
break; break;
} }
elseif($target_item['parent'] != $target_item['id']) { elseif($target_item['uri'] !== $target_item['parent-uri']) {
// we are the relay - send comments, likes and unlikes to our conversants // we are the relay - send comments, likes and relayable_retractions
// (of comments and likes) to our conversants
diaspora_send_relay($target_item,$owner,$contact); diaspora_send_relay($target_item,$owner,$contact);
break; break;
} }
@ -858,6 +860,13 @@ function notifier_run($argv, $argc){
} }
// If the item was deleted, clean up the `sign` table
if($target_item['deleted']) {
$r = q("DELETE FROM sign where `retract_iid` = %d",
intval($target_item['id'])
);
}
logger('notifier: calling hooks', LOGGER_DEBUG); logger('notifier: calling hooks', LOGGER_DEBUG);
if($normal_mode) if($normal_mode)

View File

@ -737,16 +737,16 @@ function item_post(&$a) {
if($datarray['verb'] === ACTIVITY_LIKE) if($datarray['verb'] === ACTIVITY_LIKE)
$signed_text = $datarray['guid'] . ';' . 'Post' . ';' . $parent_item['guid'] . ';' . 'true' . ';' . $myaddr; $signed_text = $datarray['guid'] . ';' . 'Post' . ';' . $parent_item['guid'] . ';' . 'true' . ';' . $myaddr;
else else
$signed_text = $datarray['guid'] . ';' . $parent_item['guid'] . ';' . $signed_body . ';' . $myaddr; $signed_text = $datarray['guid'] . ';' . $parent_item['guid'] . ';' . $signed_body . ';' . $myaddr;
$authorsig = base64_encode(rsa_sign($signed_text,$a->user['prvkey'],'sha256')); $authorsig = base64_encode(rsa_sign($signed_text,$a->user['prvkey'],'sha256'));
q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
intval($post_id), intval($post_id),
dbesc($signed_text), dbesc($signed_text),
dbesc(base64_encode($authorsig)), dbesc(base64_encode($authorsig)),
dbesc($myaddr) dbesc($myaddr)
); );
} }
} }
else { else {

125
mod/like.php Normal file → Executable file
View File

@ -104,7 +104,8 @@ function like_content(&$a) {
return; return;
} }
$r = q("SELECT `id` FROM `item` WHERE `verb` = '%s' AND `deleted` = 0
$r = q("SELECT * FROM `item` WHERE `verb` = '%s' AND `deleted` = 0
AND `contact-id` = %d AND ( `parent` = '%s' OR `parent-uri` = '%s') LIMIT 1", AND `contact-id` = %d AND ( `parent` = '%s' OR `parent-uri` = '%s') LIMIT 1",
dbesc($activity), dbesc($activity),
intval($contact['id']), intval($contact['id']),
@ -112,13 +113,70 @@ function like_content(&$a) {
dbesc($item_id) dbesc($item_id)
); );
if(count($r)) { if(count($r)) {
$like_item = $r[0];
// Already voted, undo it // Already voted, undo it
$r = q("UPDATE `item` SET `deleted` = 1, `changed` = '%s' WHERE `id` = %d LIMIT 1", $r = q("UPDATE `item` SET `deleted` = 1, `changed` = '%s' WHERE `id` = %d LIMIT 1",
dbesc(datetime_convert()), dbesc(datetime_convert()),
intval($r[0]['id']) intval($like_item['id'])
); );
proc_run('php',"include/notifier.php","like","$post_id"); // Clean up the `sign` table
$r = q("DELETE FROM `sign` WHERE `iid` = %d",
intval($like_item['id'])
);
// Save the author information for the unlike in case we need to relay to Diaspora
// Note that we can only create a signature for a user of the local server. We don't have
// a key for remote users. That is ok, because if a remote user is "unlike"ing a post, it
// means we are the relay, and for relayable_retractions, Diaspora
// only checks the parent_author_signature if it doesn't have to relay further
//
// If $item['resource-id'] exists, it means the item is a photo. Diaspora doesn't support
// likes on photos, so don't bother.
if(($activity === ACTIVITY_LIKE) && (! $item['resource-id'])) {
$signed_text = $like_item['guid'] . ';' . 'Like';
if( $contact['network'] === NETWORK_DIASPORA)
$diaspora_handle = $contact['addr'];
else { // Only works for NETWORK_DFRN
$contact_baseurl_start = strpos($contact['url'],'://') + 3;
$contact_baseurl_length = strpos($contact['url'],'/profile') - $contact_baseurl_start;
$contact_baseurl = substr($contact['url'], $contact_baseurl_start, $contact_baseurl_length);
$diaspora_handle = $contact['nick'] . '@' . $contact_baseurl;
// Get contact's private key if he's a user of the local Friendica server
$r = q("SELECT `contact`.`uid` FROM `contact` WHERE `url` = '%s' AND `self` = 1 LIMIT 1",
dbesc($contact['url'])
);
if( $r) {
$contact_uid = $r['uid'];
$r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
intval($contact_uid)
);
if( $r)
$authorsig = base64_encode(rsa_sign($signed_text,$r['prvkey'],'sha256'));
}
}
if(! isset($authorsig))
$authorsig = '';
q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
intval($like_item['id']),
dbesc($signed_text),
dbesc($authorsig),
dbesc($diaspora_handle)
);
}
// proc_run('php',"include/notifier.php","like","$post_id"); // $post_id isn't defined here!
$like_item_id = $like_item['id'];
proc_run('php',"include/notifier.php","like","$like_item_id");
return; return;
} }
@ -191,6 +249,65 @@ EOT;
); );
} }
// Save the author information for the like in case we need to relay to Diaspora
// Note that we can only create a signature for a user of the local server. We don't have
// a key for remote users. That is ok, because if a remote user is "unlike"ing a post, it
// means we are the relay, and for relayable_retractions, Diaspora
// only checks the parent_author_signature if it doesn't have to relay further
if(($activity === ACTIVITY_LIKE) && ($post_type === t('status'))) {
if( $contact['network'] === NETWORK_DIASPORA)
$diaspora_handle = $contact['addr'];
else { // Only works for NETWORK_DFRN
$contact_baseurl_start = strpos($contact['url'],'://') + 3;
$contact_baseurl_length = strpos($contact['url'],'/profile') - $contact_baseurl_start;
$contact_baseurl = substr($contact['url'], $contact_baseurl_start, $contact_baseurl_length);
$diaspora_handle = $contact['nick'] . '@' . $contact_baseurl;
// Get contact's private key if he's a user of the local Friendica server
$r = q("SELECT `contact`.`uid` FROM `contact` WHERE `url` = '%s' AND `self` = 1 LIMIT 1",
dbesc($contact['url'])
);
if( $r) {
$contact_uid = $r['uid'];
$r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1",
intval($contact_uid)
);
if( $r)
$contact_uprvkey = $r['prvkey'];
}
}
$r = q("SELECT guid, parent FROM `item` WHERE id = %d LIMIT 1",
intval($post_id)
);
if( $r) {
$p = q("SELECT guid FROM `item` WHERE id = %d AND parent = %d LIMIT 1",
intval($r[0]['parent']),
intval($r[0]['parent'])
);
if( $p) {
$signed_text = $r[0]['guid'] . ';Post;' . $p[0]['guid'] . ';true;' . $diaspora_handle;
if(isset($contact_uprvkey))
$authorsig = base64_encode(rsa_sign($signed_text,$contact_uprvkey,'sha256'));
else
$authorsig = '';
q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
intval($post_id),
dbesc($signed_text),
dbesc($authorsig),
dbesc($diaspora_handle)
);
}
}
}
$arr['id'] = $post_id; $arr['id'] = $post_id;
call_hooks('post_local_end', $arr); call_hooks('post_local_end', $arr);
@ -199,4 +316,4 @@ EOT;
killme(); killme();
// return; // NOTREACHED // return; // NOTREACHED
} }

View File

@ -1,6 +1,6 @@
<?php <?php
define( 'UPDATE_VERSION' , 1147 ); define( 'UPDATE_VERSION' , 1148 );
/** /**
* *
@ -1137,8 +1137,8 @@ INDEX ( `username` )
} }
function update_1133() { function update_1133() {
q("ALTER TABLE `user` ADD `unkmail` TINYINT( 1 ) NOT NULL DEFAULT '0' AFTER `blocktags` , ADD INDEX ( `unkmail` ) "); q("ALTER TABLE `user` ADD `unkmail` TINYINT( 1 ) NOT NULL DEFAULT '0' AFTER `blocktags` , ADD INDEX ( `unkmail` ) ");
q("ALTER TABLE `user` ADD `cntunkmail` INT NOT NULL DEFAULT '10' AFTER `unkmail` , ADD INDEX ( `cntunkmail` ) "); q("ALTER TABLE `user` ADD `cntunkmail` INT NOT NULL DEFAULT '10' AFTER `unkmail` , ADD INDEX ( `cntunkmail` ) ");
q("ALTER TABLE `mail` ADD `unknown` TINYINT( 1 ) NOT NULL DEFAULT '0' AFTER `replied` , ADD INDEX ( `unknown` ) "); q("ALTER TABLE `mail` ADD `unknown` TINYINT( 1 ) NOT NULL DEFAULT '0' AFTER `replied` , ADD INDEX ( `unknown` ) ");
} }
@ -1274,4 +1274,12 @@ function update_1146() {
return UPDATE_SUCCESS ; return UPDATE_SUCCESS ;
} }
function update_1147() {
$r1 = q("ALTER TABLE `sign` ALTER `iid` SET DEFAULT '0'");
$r2 = q("ALTER TABLE `sign` ADD `retract_iid` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `iid`");
$r3 = q("ALTER TABLE `sign` ADD INDEX ( `retract_iid` )");
if((! $r1) || (! $r2) || (! $r3))
return UPDATE_FAILED ;
return UPDATE_SUCCESS ;
}

4
view/diaspora_like_relay.tpl Normal file → Executable file
View File

@ -5,9 +5,9 @@
<target_type>$target_type</target_type> <target_type>$target_type</target_type>
<parent_guid>$parent_guid</parent_guid> <parent_guid>$parent_guid</parent_guid>
<parent_author_signature>$parentsig</parent_author_signature> <parent_author_signature>$parentsig</parent_author_signature>
<author_signature>$authrosig</author_signature> <author_signature>$authorsig</author_signature>
<positive>$positive</positive> <positive>$positive</positive>
<diaspora_handle>$handle</diaspora_handle> <diaspora_handle>$handle</diaspora_handle>
</like> </like>
</post> </post>
</XML> </XML>

View File

@ -0,0 +1,10 @@
<XML>
<post>
<relayable_retraction>
<target_type>$type</target_type>
<target_guid>$guid</target_guid>
<target_author_signature>$signature</target_author_signature>
<sender_handle>$handle</sender_handle>
</relayable_retraction>
</post>
</XML>

View File

@ -0,0 +1,11 @@
<XML>
<post>
<relayable_retraction>
<target_type>$target_type</target_type>
<target_guid>$guid</target_guid>
<parent_author_signature>$parentsig</parent_author_signature>
<target_author_signature>$authorsig</target_author_signature>
<sender_handle>$handle</sender_handle>
</relayable_retraction>
</post>
</XML>