diff --git a/include/diaspora.php b/include/diaspora.php
index 0764dfa4ae..e089e3f040 100644
--- a/include/diaspora.php
+++ b/include/diaspora.php
@@ -114,15 +114,29 @@ EOT;
}
+/**
+ *
+ * diaspora_decode($importer,$xml)
+ * array $importer -> from user table
+ * string $xml -> urldecoded Diaspora salmon
+ *
+ * Returns array
+ * 'message' -> decoded Diaspora XML message
+ * 'author' -> author diaspora handle
+ * 'key' -> author public key (converted to pkcs#8)
+ *
+ * Author and key are used elsewhere to save a lookup for verifying replies and likes
+ */
+
function diaspora_decode($importer,$xml) {
-
-
$basedom = parse_xml_string($xml);
$atom = $basedom->children(NAMESPACE_ATOM1);
+ // Diaspora devs: This is kind of sucky - 'encrypted_header' does not belong in the atom namespace
+
$encrypted_header = json_decode(base64_decode($atom->encrypted_header));
$encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key);
@@ -185,7 +199,14 @@ function diaspora_decode($importer,$xml) {
// strip whitespace so our data element will return to one big base64 blob
$data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data);
+
// Add back the 60 char linefeeds
+
+ // Diaspora devs: This completely violates the entire principle of salmon magic signatures,
+ // which was to have a message signing format that was completely ambivalent to linefeeds
+ // and transport whitespace mangling, and base64 wrapping rules. Guess what? PHP and Ruby
+ // use different linelengths for base64 output.
+
$lines = str_split($data,60);
$data = implode("\n",$lines);
@@ -197,6 +218,8 @@ function diaspora_decode($importer,$xml) {
$encoding = $base->encoding;
$alg = $base->alg;
+ // Diaspora devs: I can't even begin to tell you how sucky this is. Read the freaking spec.
+
$signed_data = $data . (($data[-1] != "\n") ? "\n" : '') . '.' . base64url_encode($type) . "\n" . '.' . base64url_encode($encoding) . "\n" . '.' . base64url_encode($alg) . "\n";
@@ -255,6 +278,20 @@ function diaspora_get_contact_by_handle($uid,$handle) {
return false;
}
+function find_person_by_handle($handle) {
+ // we don't care about the uid, we just want to save an expensive webfinger probe
+ $r = q("select * from contact where network = '%s' and addr = '%s' LIMIT 1",
+ dbesc(NETWORK_DIASPORA),
+ dbesc($handle)
+ );
+ if(count($r))
+ return $r[0];
+ $r = probe_url($handle);
+ // need to cached this, perhaps in fcontact
+ if(count($r))
+ return ($r);
+ return false;
+}
function diaspora_request($importer,$xml) {
@@ -266,7 +303,12 @@ function diaspora_request($importer,$xml) {
$contact = diaspora_get_contact_by_handle($importer['uid'],$sender_handle);
+
if($contact) {
+
+ // perhaps we were already sharing with this person. Now they're sharing with us.
+ // That makes us friends.
+
if($contact['rel'] == CONTACT_IS_FOLLOWER) {
q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d AND `uid` = %d LIMIT 1",
intval(CONTACT_IS_FRIEND),
@@ -414,11 +456,19 @@ function diaspora_post($importer,$xml) {
}
function diaspora_comment($importer,$xml,$msg) {
+
$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));
+ $text = unxmlify($xml->text);
+ $author_signature = notags(unxmlify($xml->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);
+ $text = $xml->text;
+
+ $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']);
if(! $contact)
return;
@@ -428,24 +478,109 @@ function diaspora_comment($importer,$xml,$msg) {
// NOTREACHED
}
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
+ intval($importer['uid']),
+ dbesc($parent_guid)
+ );
+ if(! count($r)) {
+ logger('diaspora_comment: parent item not found: ' . $guid);
+ return;
+ }
+ $parent_item = $r[0];
+ $author_signed_data = $guid . ';' . $parent_guid . ';' . $text . ';' . $diaspora_handle;
+
+ $author_signature = base64_decode($author_signature);
+
+ if(stricmp($diaspora_handle,$msg['author']) == 0) {
+ $person = $contact;
+ $key = $msg['key'];
+ }
+ else {
+ $person = find_person_by_handle($diaspora_handle);
+
+ if(is_array($person) && x($person,'pubkey'))
+ $key = $person['pubkey'];
+ else {
+ logger('diaspora_comment: unable to find author details');
+ return;
+ }
+ }
+
+ if(! rsa_verify($author_signed_data,$author_signature,$key)) {
+ logger('diaspora_comment: verification failed.');
+ return;
+ }
+
+ if($parent_author_signature) {
+ $owner_signed_data = $guid . ';' . $parent_guid . ';' . $text . ';' . $msg['author'];
+
+ $parent_author_signature = base64_decode($parent_author_signature);
+
+ $key = $msg['key'];
+
+ if(! rsa_verify($owner_signed_data,$parent_author_signature,$key)) {
+ logger('diaspora_comment: owner verification failed.');
+ return;
+ }
+ }
+
+ // Phew! Everything checks out. Now create an item.
+
+ require_once('library/HTMLPurifier.auto.php');
+ require_once('include/html2bbcode.php');
+
+ $body = $text;
+
+ $maxlen = get_max_import_size();
+ if($maxlen && (strlen($body) > $maxlen))
+ $body = substr($body,0, $maxlen);
+
+ if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
+
+ $body = preg_replace('##s',
+ '[youtube]$1[/youtube]', $body);
+
+ $body = preg_replace('##s',
+ '[youtube]$1[/youtube]', $body);
+
+ $body = oembed_html2bbcode($body);
+
+ $config = HTMLPurifier_Config::createDefault();
+ $config->set('Cache.DefinitionImpl', null);
+ $purifier = new HTMLPurifier($config);
+ $body = $purifier->purify($body);
+
+ $body = html2bbcode($body);
+ }
$message_id = $diaspora_handle . ':' . $guid;
- $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1",
- intval($importer['uid']),
- dbesc($guid)
- );
- if(! count($r))
- return;
- $owner = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1",
- intval($importer['uid'])
- );
- if(! count($owner))
- return;
+ $datarray = array();
+ $datarray['uid'] = $importer['uid'];
+ $datarray['contact-id'] = $contact['id'];
+ $datarray['wall'] = $parent_item['wall'];
+ $datarray['gravity'] = GRAVITY_COMMENT;
+ $datarray['guid'] = $guid;
+ $datarray['uri'] = $message_id;
+ $datarray['parent-uri'] = $parent_item['uri'];
- $created = unxmlify($xml->created_at);
- $private = ((unxmlify($xml->public) == 'false') ? 1 : 0);
+ // No timestamps for comments? OK, we'll the use current time.
+ $datarray['created'] = $datarray['edited'] = datetime_convert();
+ $datarray['private'] = $parent_item['private'];
+
+ $datarray['owner-name'] = $contact['name'];
+ $datarray['owner-link'] = $contact['url'];
+ $datarray['owner-avatar'] = $contact['thumb'];
+
+ $datarray['author-name'] = $person['name'];
+ $datarray['author-link'] = $person['url'];
+ $datarray['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']);
+ $datarray['body'] = $body;
+
+ item_store($datarray);
+
+ return;
}
@@ -456,14 +591,16 @@ function diaspora_like($importer,$xml,$msg) {
$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
+ // 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);
+ $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']);
if(! $contact)
return;
@@ -512,10 +649,19 @@ function diaspora_like($importer,$xml,$msg) {
$author_signature = base64_decode($author_signature);
- if(stricmp($diaspora_handle,$msg['author']) == 0)
+ if(stricmp($diaspora_handle,$msg['author']) == 0) {
+ $person = $contact;
$key = $msg['key'];
- else
- $key = get_diaspora_key($diaspora_handle);
+ }
+ else {
+ $person = find_person_by_handle($diaspora_handle);
+ if(is_array($person) && x($person,'pubkey'))
+ $key = $person['pubkey'];
+ else {
+ logger('diaspora_comment: unable to find author details');
+ return;
+ }
+ }
if(! rsa_verify($author_signed_data,$author_signature,$key)) {
logger('diaspora_like: verification failed.');
@@ -539,6 +685,7 @@ function diaspora_like($importer,$xml,$msg) {
$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('' . "\n") ;
@@ -568,26 +715,23 @@ EOT;
$arr['parent'] = $parent_item['id'];
$arr['parent-uri'] = $parent_item['uri'];
-// $arr['owner-name'] = $owner['name']; // FIXME
-// $arr['owner-link'] = $owner['url'];
-// $arr['owner-avatar'] = $owner['thumb'];
+ $datarray['owner-name'] = $contact['name'];
+ $datarray['owner-link'] = $contact['url'];
+ $datarray['owner-avatar'] = $contact['thumb'];
- $arr['author-name'] = $contact['name'];
- $arr['author-link'] = $contact['url'];
- $arr['author-avatar'] = $contact['thumb'];
+ $datarray['author-name'] = $person['name'];
+ $datarray['author-link'] = $person['url'];
+ $datarray['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['private'] = $parent_item['private'];
$arr['verb'] = $activity;
$arr['object-type'] = $objtype;
$arr['object'] = $obj;
- $arr['allow_cid'] = $parent_item['allow_cid'];
- $arr['allow_gid'] = $parent_item['allow_gid'];
- $arr['deny_cid'] = $parent_item['deny_cid'];
- $arr['deny_gid'] = $parent_item['deny_gid'];
$arr['visible'] = 1;
$arr['unseen'] = 1;
$arr['last-child'] = 0;