aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Macgirvin <mike@macgirvin.com>2010-10-24 20:39:24 -0700
committerMike Macgirvin <mike@macgirvin.com>2010-10-24 20:39:24 -0700
commitb41218ca303b9fd8258fd613915d3c4b9fd411c0 (patch)
treec1b08bee4f7b3737e3717d4d5eb4be9b781dc044
parentb8b227b32882fb511c8481a41c53637e7ce7707a (diff)
downloadvolse-hubzilla-b41218ca303b9fd8258fd613915d3c4b9fd411c0.tar.gz
volse-hubzilla-b41218ca303b9fd8258fd613915d3c4b9fd411c0.tar.bz2
volse-hubzilla-b41218ca303b9fd8258fd613915d3c4b9fd411c0.zip
workflow for federated/non-dfrn followers
-rw-r--r--boot.php64
-rw-r--r--include/items.php201
-rw-r--r--include/notifier.php12
-rw-r--r--include/poller.php121
-rw-r--r--include/salmon.php114
-rw-r--r--mod/contacts.php1
-rw-r--r--mod/dfrn_confirm.php378
-rw-r--r--mod/dfrn_notify.php6
-rw-r--r--mod/network.php2
-rw-r--r--mod/notifications.php31
-rw-r--r--mod/salmon.php10
-rw-r--r--view/acl_selectors.php2
-rw-r--r--view/intros.tpl32
-rw-r--r--view/magicsig.tpl9
-rw-r--r--view/netfriend.tpl14
-rw-r--r--view/theme/default/style.css9
16 files changed, 647 insertions, 359 deletions
diff --git a/boot.php b/boot.php
index 7885bb4ea..8c9248703 100644
--- a/boot.php
+++ b/boot.php
@@ -52,10 +52,12 @@ define ( 'NAMESPACE_DFRN' , 'http://purl.org/macgirvin/dfrn/1.0' );
define ( 'NAMESPACE_THREAD' , 'http://purl.org/syndication/thread/1.0' );
define ( 'NAMESPACE_TOMB' , 'http://purl.org/atompub/tombstones/1.0' );
define ( 'NAMESPACE_ACTIVITY', 'http://activitystrea.ms/spec/1.0/' );
-define ( 'NAMESPACE_ACTIVITY_SCHEMA', 'http://activitystrea.ms/schema/1.0/');
-define ( 'NAMESPACE_SALMON_ME', 'http://salmon-protocol.org/ns/magic-env');
-define ( 'NAMESPACE_OSTATUSSUB', 'http://ostatus.org/schema/1.0/subscribe');
-define ( 'NAMESPACE_GEORSS', 'http://www.georss.org/georss');
+define ( 'NAMESPACE_ACTIVITY_SCHEMA', 'http://activitystrea.ms/schema/1.0/' );
+define ( 'NAMESPACE_SALMON_ME', 'http://salmon-protocol.org/ns/magic-env' );
+define ( 'NAMESPACE_OSTATUSSUB', 'http://ostatus.org/schema/1.0/subscribe' );
+define ( 'NAMESPACE_GEORSS', 'http://www.georss.org/georss' );
+define ( 'NAMESPACE_POCO', 'http://portablecontacts.net/spec/1.0' );
+define ( 'NAMESPACE_FEED', 'http://schemas.google.com/g/2010#updates-from' );
// activity stream defines
@@ -961,11 +963,11 @@ function webfinger($s) {
if(strlen($host)) {
$tpl = fetch_lrdd_template($host);
if(strlen($tpl)) {
- $pxrd = str_replace('{uri}', urlencode('acct://'.$s), $tpl);
+ $pxrd = str_replace('{uri}', urlencode('acct:'.$s), $tpl);
$links = fetch_xrd_links($pxrd);
if(! count($links)) {
- // try without the double slashes
- $pxrd = str_replace('{uri}', urlencode('acct:'.$s), $tpl);
+ // try with double slashes
+ $pxrd = str_replace('{uri}', urlencode('acct://'.$s), $tpl);
$links = fetch_xrd_links($pxrd);
}
return $links;
@@ -974,6 +976,54 @@ function webfinger($s) {
return array();
}}
+if(! function_exists('lrdd')) {
+function lrdd($uri) {
+
+ $a = get_app();
+
+ if(strstr($uri,'@')) {
+ return(webfinger($uri));
+ }
+ else {
+ $html = fetch_url($uri);
+ $headers = $a->get_curl_headers();
+ $lines = explode("\n",$headers);
+ if(count($lines)) {
+ foreach($lines as $line) {
+ // TODO alter the following regex to support multiple relations (space separated)
+ if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) {
+ $link = $matches[1];
+ break;
+ }
+ }
+ }
+ if(! isset($link)) {
+ // parse the page of the supplied URL looking for rel links
+
+ require_once('library/HTML5/Parser.php');
+ $dom = HTML5_Parser::parse($html);
+
+ if($dom) {
+ $items = $dom->getElementsByTagName('link');
+
+ foreach($items as $item) {
+ $x = $item->getAttribute('rel');
+ if($x == "lrdd") {
+ $link = $item->getAttribute('href');
+ break;
+ }
+ }
+ }
+ }
+
+ if(isset($link))
+ return(fetch_xrd_links($link));
+ }
+ return array();
+}}
+
+
+
// Given a host name, locate the LRDD template from that
// host. Returns the LRDD template or an empty string on
// error/failure.
diff --git a/include/items.php b/include/items.php
index e44892316..6cdf5f0f5 100644
--- a/include/items.php
+++ b/include/items.php
@@ -267,28 +267,89 @@ function construct_activity($item) {
-function get_atom_elements($item) {
+function get_atom_elements($feed,$item) {
require_once('library/HTMLPurifier.auto.php');
require_once('include/html2bbcode.php');
- $res = array();
+ $best_photo = array();
- $raw_author = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
- if($raw_author) {
- if($raw_author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['rel'] === 'photo')
- $res['author-avatar'] = unxmlify($raw_author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['href']);
- }
+ $res = array();
$author = $item->get_author();
$res['author-name'] = unxmlify($author->get_name());
$res['author-link'] = unxmlify($author->get_link());
- if(! $res['author-avatar'])
- $res['author-avatar'] = unxmlify($author->get_avatar());
$res['uri'] = unxmlify($item->get_id());
$res['title'] = unxmlify($item->get_title());
$res['body'] = unxmlify($item->get_content());
+
+ // look for a photo. We should check media size and find the best one,
+ // but for now let's just find any author photo
+
+ $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
+
+ if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
+ $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
+ foreach($base as $link) {
+ if(! $res['author-avatar']) {
+ if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
+ $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ }
+ }
+ }
+
+ $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor');
+
+ if($rawactor && $rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] === ACTIVITY_OBJ_PERSON) {
+ $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
+ if($base && count($base)) {
+ foreach($base as $link) {
+ if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
+ $res['author-link'] = unxmlify($link['attribs']['']['href']);
+ if(! $res['author-avatar']) {
+ if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
+ $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ }
+ }
+ }
+ }
+
+ // No photo/profile-link on the item - look at the feed level
+
+ if((! $res['author-link']) || (! $res['author-avatar'])) {
+ $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
+ if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
+ $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
+ foreach($base as $link) {
+ if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
+ $res['author-link'] = unxmlify($link['attribs']['']['href']);
+ if(! $res['author-avatar']) {
+ if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
+ $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ }
+ }
+ }
+
+ $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject');
+
+ if($rawactor && $rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] === ACTIVITY_OBJ_PERSON) {
+ $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
+
+ if($base && count($base)) {
+ foreach($base as $link) {
+ if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
+ $res['author-link'] = unxmlify($link['attribs']['']['href']);
+ if(! $res['author-avatar']) {
+ if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
+ $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
+ }
+ }
+ }
+ }
+ }
+
+
$maxlen = get_max_import_size();
if($maxlen && (strlen($res['body']) > $maxlen))
$res['body'] = substr($res['body'],0, $maxlen);
@@ -310,7 +371,7 @@ function get_atom_elements($item) {
'[youtube]$1[/youtube]', $res['body']);
$config = HTMLPurifier_Config::createDefault();
- $config->set('Core.DefinitionCache', null);
+ $config->set('Cache.DefinitionImpl', null);
// we shouldn't need a whitelist, because the bbcode converter
// will strip out any unsupported tags.
@@ -353,27 +414,21 @@ function get_atom_elements($item) {
elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
$res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
- if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['rel'] === 'photo')
- $res['owner-avatar'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['href']);
- elseif($rawowner[0]['child'][NAMESPACE_DFRN]['avatar'][0]['data'])
- $res['owner-avatar'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['avatar'][0]['data']);
+ if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
+ $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
+
+ foreach($base as $link) {
+ if(! $res['owner-avatar']) {
+ if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
+ $res['owner-avatar'] = unxmlify($link['attribs']['']['href']);
+ }
+ }
+ }
$rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point');
if($rawgeo)
$res['coord'] = unxmlify($rawgeo[0]['data']);
- $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
- if($rawactor && $rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] === ACTIVITY_OBJ_PERSON) {
- $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
- if($base && count($base)) {
- foreach($base as $link) {
- if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
- $res['author-link'] = unxmlify($link['attribs']['']['href']);
- if($link['attribs']['']['rel'] === 'avatar' && (! $res['author-avatar']))
- $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
- }
- }
- }
$rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb');
// select between supported verbs
@@ -405,7 +460,7 @@ function get_atom_elements($item) {
'[youtube]$1[/youtube]', $body);
$config = HTMLPurifier_Config::createDefault();
- $config->set('Core.DefinitionCache', null);
+ $config->set('Cache.DefinitionImpl', null);
$purifier = new HTMLPurifier($config);
$body = $purifier->purify($body);
@@ -423,9 +478,6 @@ function get_atom_elements($item) {
function item_store($arr) {
-//print_r($arr);
-
-
if($arr['gravity'])
$arr['gravity'] = intval($arr['gravity']);
elseif($arr['parent-uri'] == $arr['uri'])
@@ -648,6 +700,7 @@ function consume_feed($xml,$importer,$contact, &$hub) {
$feed->init();
// Check at the feed level for updated contact name and/or photo
+ $debugging = get_config('system','debugging');
$name_updated = '';
$new_name = '';
@@ -832,11 +885,13 @@ function consume_feed($xml,$importer,$contact, &$hub) {
}
continue;
}
- $datarray = get_atom_elements($item);
+ $datarray = get_atom_elements($feed,$item);
+ if($contact['network'] === 'stat' && strlen($datarray['title']))
+ unset($datarray['title']);
$datarray['parent-uri'] = $parent_uri;
$datarray['uid'] = $importer['uid'];
$datarray['contact-id'] = $contact['id'];
- if(($datarray['verb'] == ACTIVITY_LIKE) || ($datarray['verb'] == ACTIVITY_DISLIKE)) {
+ if(($datarray['verb'] === ACTIVITY_LIKE) || ($datarray['verb'] === ACTIVITY_DISLIKE)) {
$datarray['type'] = 'activity';
$datarray['gravity'] = GRAVITY_LIKE;
}
@@ -865,17 +920,23 @@ function consume_feed($xml,$importer,$contact, &$hub) {
}
continue;
}
- $datarray = get_atom_elements($item);
- if(($datarray['verb'] === ACTIVITY_FOLLOW) && (! is_array($contact))) {
- new_follower($importer,$datarray);
+ $datarray = get_atom_elements($feed,$item);
+
+ if($datarray['verb'] === ACTIVITY_FOLLOW) {
+ if($debugging)
+ file_put_contents('salmon.out',"\n" . 'New follower.' . "\n", FILE_APPEND);
+ new_follower($importer,$contact,$datarray,$item);
return;
}
if($datarray['verb'] === ACTIVITY_UNFOLLOW) {
- lose_follower($importer,$contact,$datarray);
+ lose_follower($importer,$contact,$datarray,$item);
return;
}
if(! is_array($contact))
return;
+
+ if($contact['network'] === 'stat' && strlen($datarray['title']))
+ unset($datarray['title']);
$datarray['parent-uri'] = $item_id;
$datarray['uid'] = $importer['uid'];
$datarray['contact-id'] = $contact['id'];
@@ -888,14 +949,76 @@ function consume_feed($xml,$importer,$contact, &$hub) {
}
-function new_follower($importer,$datarray) {
+function new_follower($importer,$contact,$datarray,$item) {
+ $url = notags(trim($datarray['author-link']));
+ $name = notags(trim($datarray['author-name']));
+ $photo = notags(trim($datarray['author-avatar']));
+
+ $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY,'actor');
+ if($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'])
+ $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'];
+
+ if(is_array($contact)) {
+ if($contact['network'] == 'stat' && $contact['rel'] == REL_FAN) {
+ $q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ intval(REL_BUD),
+ intval($contact['id']),
+ intval($importer['uid'])
+ );
+ }
+ // send email notification to owner?
+ }
+ else {
+
+ // create contact record - set to readonly
+ $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `nick`, `photo`, `network`, `rel`,
+ `blocked`, `readonly`, `pending` )
+ VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 1, 1 ) ",
+ intval($importer['uid']),
+ dbesc(datetime_convert()),
+ dbesc($url),
+ dbesc($name),
+ dbesc($nick),
+ dbesc($photo),
+ dbesc('stat'),
+ intval(REL_VIP)
+ );
+ $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `pending` = 1 AND `rel` = %d LIMIT 1",
+ intval($importer['uid']),
+ dbesc($url),
+ intval(REL_VIP)
+ );
+ if(count($r))
+ $contact_record = $r[0];
+
+ // create notification
+ $hash = random_string();
+
+ if(is_array($contact_record)) {
+ $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `hash`, `datetime`)
+ VALUES ( %d, %d, 0, 0, '%s', '%s' )",
+ intval($importer['uid']),
+ intval($contact_record['id']),
+ dbesc($hash),
+ dbesc(datetime_convert())
+ );
+ }
+ }
}
-function lose_follower($importer,$contact,$datarray) {
-
+function lose_follower($importer,$contact,$datarray,$item) {
+ if($contact['rel'] == REL_BUD) {
+ q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d LIMIT 1",
+ intval(REL_FAN),
+ intval($contact['id'])
+ );
+ }
+ else {
+ contact_remove($contact['id']);
+ }
}
diff --git a/include/notifier.php b/include/notifier.php
index 99b385996..9e4a7102e 100644
--- a/include/notifier.php
+++ b/include/notifier.php
@@ -87,6 +87,9 @@
// If this is a public conversation, notify the feed hub
$notify_hub = true;
+ // fill this in with a salmon slap if applicable
+ $slap = '';
+
if($cmd != 'mail') {
require_once('include/group.php');
@@ -197,7 +200,7 @@
$actobj = construct_activity($item);
if($item['id'] == $item_id) {
- $atom .= replace_macros($cmnt_template, array(
+ $slap = replace_macros($cmnt_template, array(
'$name' => xmlify($owner['name']),
'$profile_page' => xmlify($owner['url']),
'$thumb' => xmlify($owner['thumb']),
@@ -220,6 +223,7 @@
));
}
}
+ $atom .= $slap;
}
else {
foreach($items as $item) {
@@ -303,6 +307,8 @@
// delivery loop
+
+
foreach($r as $contact) {
if($contact['self'])
continue;
@@ -314,6 +320,10 @@
$deliver_status = dfrn_deliver($owner,$contact,$atom,$debugging);
break;
default:
+ if($followup) {
+ require_once('include/salmon.php');
+ slapper($owner,$contact,$slap);
+ }
break;
}
diff --git a/include/poller.php b/include/poller.php
index 720163fef..b88b131e2 100644
--- a/include/poller.php
+++ b/include/poller.php
@@ -22,7 +22,8 @@
$a->set_baseurl(get_config('system','url'));
$contacts = q("SELECT * FROM `contact`
- WHERE `network` = 'dfrn' AND ( `dfrn-id` != '' OR (`issued-id` != '' AND `duplex` = 1))
+ WHERE ( ( `network` = 'dfrn' AND ( `dfrn-id` != '' OR (`issued-id` != '' AND `duplex` = 1)))
+ OR ( `network` = 'stat' AND `poll` != '' ) )
AND `self` = 0 AND `blocked` = 0 AND `readonly` = 0 ORDER BY RAND()");
if(! count($contacts))
@@ -88,79 +89,86 @@
: datetime_convert('UTC','UTC',$contact['last-update'], ATOM_TIME)
);
- $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
+ if($contact['network'] === 'dfrn') {
- if(intval($contact['duplex']) && $contact['dfrn-id'])
- $idtosend = '0:' . $orig_id;
- if(intval($contact['duplex']) && $contact['issued-id'])
- $idtosend = '1:' . $orig_id;
+ $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
- $url = $contact['poll'] . '?dfrn_id=' . $idtosend
- . '&dfrn_version=' . DFRN_PROTOCOL_VERSION
- . '&type=data&last_update=' . $last_update ;
+ if(intval($contact['duplex']) && $contact['dfrn-id'])
+ $idtosend = '0:' . $orig_id;
+ if(intval($contact['duplex']) && $contact['issued-id'])
+ $idtosend = '1:' . $orig_id;
- $xml = fetch_url($url);
+ $url = $contact['poll'] . '?dfrn_id=' . $idtosend
+ . '&dfrn_version=' . DFRN_PROTOCOL_VERSION
+ . '&type=data&last_update=' . $last_update ;
+
+ $xml = fetch_url($url);
- if($debugging) {
- echo "URL: " . $url . "\n";
- echo "XML: " . $xml . "\n";
- }
+ if($debugging) {
+ echo "URL: " . $url . "\n";
+ echo "XML: " . $xml . "\n";
+ }
- if(! $xml) {
- // dead connection - might be a transient event, or this might
- // mean the software was uninstalled or the domain expired.
- // Will keep trying for one month.
- mark_for_death($contact);
- continue;
- }
+ if(! $xml) {
+ // dead connection - might be a transient event, or this might
+ // mean the software was uninstalled or the domain expired.
+ // Will keep trying for one month.
+ mark_for_death($contact);
+ continue;
+ }
- $res = simplexml_load_string($xml);
+ $res = simplexml_load_string($xml);
- if(intval($res->status) == 1) {
- // we may not be friends anymore. Will keep trying for one month.
- mark_for_death($contact);
- }
- else {
- if($contact['term-date'] != '0000-00-00 00:00:00')
- unmark_for_death($contact);
- }
+ if(intval($res->status) == 1) {
+ // we may not be friends anymore. Will keep trying for one month.
+ mark_for_death($contact);
+ }
+ else {
+ if($contact['term-date'] != '0000-00-00 00:00:00')
+ unmark_for_death($contact);
+ }
- if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id)))
- continue;
+ if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id)))
+ continue;
- $postvars = array();
+ $postvars = array();
- $sent_dfrn_id = hex2bin($res->dfrn_id);
- $challenge = hex2bin($res->challenge);
+ $sent_dfrn_id = hex2bin($res->dfrn_id);
+ $challenge = hex2bin($res->challenge);
- $final_dfrn_id = '';
+ $final_dfrn_id = '';
- if(($contact['duplex']) && strlen($contact['prvkey'])) {
- openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
- openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']);
+ if(($contact['duplex']) && strlen($contact['prvkey'])) {
+ openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
+ openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']);
+ }
+ else {
+ openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
+ openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']);
+ }
- }
- else {
- openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
- openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']);
- }
+ $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
+
+ if(strpos($final_dfrn_id,':') == 1)
+ $final_dfrn_id = substr($final_dfrn_id,2);
- $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
+ if($final_dfrn_id != $orig_id) {
- if(strpos($final_dfrn_id,':') == 1)
- $final_dfrn_id = substr($final_dfrn_id,2);
+ // did not decode properly - cannot trust this site
+ continue;
+ }
- if($final_dfrn_id != $orig_id) {
+ $postvars['dfrn_id'] = $idtosend;
+ $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
- // did not decode properly - cannot trust this site
- continue;
+ $xml = post_url($contact['poll'],$postvars);
}
+ else {
+ // $contact['network'] !== 'dfrn'
- $postvars['dfrn_id'] = $idtosend;
- $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
-
- $xml = post_url($contact['poll'],$postvars);
+ $xml = fetch_url($contact['poll']);
+ }
if($debugging) {
echo "XML response:" . $xml . "\n";
@@ -170,10 +178,8 @@
if(! strlen($xml))
continue;
-
consume_feed($xml,$importer,$contact,$hub);
-
if((strlen($hub)) && ($contact['rel'] == REL_BUD) && ($contact['priority'] == 0)) {
$hubs = explode(',', $hub);
if(count($hubs)) {
@@ -192,7 +198,8 @@
intval($contact['id'])
);
- }
+ // loop - next contact
+ }
killme();
diff --git a/include/salmon.php b/include/salmon.php
index 92ec571b3..c5ad3e825 100644
--- a/include/salmon.php
+++ b/include/salmon.php
@@ -33,61 +33,9 @@ function get_salmon_key($uri,$keyhash) {
if($debugging)
file_put_contents('salmon.out', "\n" . 'Fetch key' . "\n", FILE_APPEND);
- if(strstr($uri,'@')) {
- $arr = webfinger($uri);
- if($debugging)
- file_put_contents('salmon.out', "\n" . 'Fetch key from webfinger' . "\n", FILE_APPEND);
- }
- else {
- $html = fetch_url($uri);
- $a = get_app();
- $h = $a->get_curl_headers();
- if($debugging)
- file_put_contents('salmon.out', "\n" . 'Fetch key via HTTP header: ' . $h . "\n", FILE_APPEND);
-
- $l = explode("\n",$h);
- if(count($l)) {
- foreach($l as $line) {
- // TODO alter the following regex to support multiple relations (space separated)
- if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) {
- $link = $matches[1];
- if($debugging)
- file_put_contents('salmon.out', "\n" . 'Fetch key via HTML Link: ' . $link . "\n", FILE_APPEND);
- break;
- }
- }
- }
-
- if(! isset($link)) {
-
- // parse the page of the supplied URL looking for rel links
-
- require_once('library/HTML5/Parser.php');
- $dom = HTML5_Parser::parse($html);
-
- if(! $dom)
- return '';
+ $arr = lrdd($uri);
- $items = $dom->getElementsByTagName('link');
-
- foreach($items as $item) {
- $x = $item->getAttribute('rel');
- if($x == "lrdd") {
- $link = $item->getAttribute('href');
- if($debugging)
- file_put_contents('salmon.out', "\n" . 'Fetch key via HTML body' . $link . "\n", FILE_APPEND);
- break;
- }
- }
- }
-
- if(! isset($link))
- return '';
-
- $arr = fetch_xrd_links($link);
- }
-
- if($arr) {
+ if(is_array($arr)) {
foreach($arr as $a) {
if($a['@attributes']['rel'] === 'magic-public-key') {
$ret[] = $a['@attributes']['href'];
@@ -140,4 +88,60 @@ function get_salmon_key($uri,$keyhash) {
- \ No newline at end of file
+function slapper($owner,$contact,$slap) {
+
+
+ // does contact have a salmon endpoint?
+
+ if(! strlen($contact['notify']))
+ return;
+
+ // add all namespaces to item
+
+$namespaces = <<< EOT
+<entry xmlns="http://www.w3.org/2005/Atom"
+ xmlns:thr="http://purl.org/syndication/thread/1.0"
+ xmlns:at="http://purl.org/atompub/tombstones/1.0"
+ xmlns:media="http://purl.org/syndication/atommedia"
+ xmlns:dfrn="http://purl.org/macgirvin/dfrn/1.0"
+ xmlns:as="http://activitystrea.ms/spec/1.0/"
+ xmlns:georss="http://www.georss.org/georss" >
+EOT;
+
+ $slap = str_replace('<entry>',$namespaces,$slap);
+
+ // create a magic envelope
+
+ $data = base64url_encode($slap);
+ $data_type = 'application/atom+xml';
+ $encoding = 'base64url';
+ $algorithm = 'RSA-SHA256';
+ $keyhash = base64url_encode(hash('sha256',salmon_key($owner['spubkey'])));
+
+ // Setup RSA stuff to PKCS#1 sign the data
+
+ set_include_path(get_include_path() . PATH_SEPARATOR . 'phpsec');
+
+ require_once('phpsec/Crypt/RSA.php');
+
+ $rsa = new CRYPT_RSA();
+ $rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1;
+ $rsa->setHash('sha256');
+ $rsa->loadKey($owner['sprvkey']);
+
+ $signature = $rsa->sign($data);
+
+ $salmon_tpl = load_view_file('view/magicsig.tpl');
+ $salmon = replace_macros($salmon_tpl,array(
+ '$data' => $data,
+ '$encoding' => $encoding,
+ '$algorithm' => $algorithm,
+ '$keyhash' => $keyhash,
+ '$signature' => $signature
+ ));
+
+ // slap them
+ post_url($contact['notify'],$salmon);
+
+ return;
+} \ No newline at end of file
diff --git a/mod/contacts.php b/mod/contacts.php
index 746331ea5..bd5bf8ea8 100644
--- a/mod/contacts.php
+++ b/mod/contacts.php
@@ -271,7 +271,6 @@ function contacts_content(&$a) {
default:
break;
}
-
if(($rr['network'] === 'dfrn') && ($rr['rel'])) {
$url = "redir/{$rr['id']}";
$sparkle = ' class="sparkle" ';
diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php
index b4ca74889..cc1edea22 100644
--- a/mod/dfrn_confirm.php
+++ b/mod/dfrn_confirm.php
@@ -7,7 +7,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
if(is_array($handsfree)) {
// called directly from dfrn_request due to automatic friend acceptance
- // any $_POST parameters we might need are supplied in the $handsfree array
+ // any $_POST parameters we may require are supplied in the $handsfree array
$node = $handsfree['node'];
$a->interactive = false; // notice() becomes a no-op since nobody is there to see it
@@ -19,12 +19,12 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
}
// Main entry point. Our user received a friend request notification (perhaps
- // from another site) and clicked 'Accept'. $POST['source_url'] is not set.
+ // from another site) and clicked 'Approve'. $POST['source_url'] is not set.
// OR we have been called directly from dfrn_request ($handsfree != null) due to
// this being a page type which supports automatic friend acceptance.
if(! x($_POST,'source_url')) {
-
+
$uid = ((is_array($handsfree)) ? $handsfree['uid'] : local_user());
if(! $uid) {
@@ -42,7 +42,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
}
- // These come from the friend request notification form or $handsfree reply.
+ // These come from either the friend request notification form or $handsfree array.
if(is_array($handsfree)) {
$dfrn_id = $handsfree['dfrn_id'];
@@ -53,13 +53,16 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
$dfrn_id = ((x($_POST,'dfrn_id')) ? notags(trim($_POST['dfrn_id'])) : "");
$intro_id = intval($_POST['intro_id']);
$duplex = intval($_POST['duplex']);
+ $cid = intval($_POST['contact_id']);
}
// The other person will have been issued an ID when they first requested friendship.
// Locate their record. At this time, their record will have both pending and blocked set to 1.
+ // There won't be any dfrn_id if this is a network follower, so use the contact_id instead.
- $r = q("SELECT * FROM `contact` WHERE `issued-id` = '%s' AND `uid` = %d LIMIT 1",
+ $r = q("SELECT * FROM `contact` WHERE ( ( `issued-id` != '' AND `issued-id` = '%s' ) OR ( `id` = %d AND `id` != 0 ) ) AND `uid` = %d LIMIT 1",
dbesc($dfrn_id),
+ intval($cid),
intval($uid)
);
@@ -68,153 +71,158 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
return;
}
- $contact_id = $r[0]['id'];
- $relation = $r[0]['rel'];
- $site_pubkey = $r[0]['site-pubkey'];
- $dfrn_confirm = $r[0]['confirm'];
- $aes_allow = $r[0]['aes_allow'];
+ $contact = $r[0];
+ $contact_id = $contact['id'];
+ $relation = $contact['rel'];
+ $site_pubkey = $contact['site-pubkey'];
+ $dfrn_confirm = $contact['confirm'];
+ $aes_allow = $contact['aes_allow'];
- // Generate a key pair for all further communications with this person.
- // We have a keypair for every contact, and a site key for unknown people.
- // This provides a means to carry on relationships with other people if
- // any single key is compromised. It is a robust key. We're much more
- // worried about key leakage than anybody cracking it.
- $res = openssl_pkey_new(array(
- 'digest_alg' => 'whirlpool',
- 'private_key_bits' => 4096,
- 'encrypt_key' => false )
- );
+ if($contact['network'] === 'dfrn') {
+ // Generate a key pair for all further communications with this person.
+ // We have a keypair for every contact, and a site key for unknown people.
+ // This provides a means to carry on relationships with other people if
+ // any single key is compromised. It is a robust key. We're much more
+ // worried about key leakage than anybody cracking it.
- $private_key = '';
+ $res = openssl_pkey_new(array(
+ 'digest_alg' => 'whirlpool',
+ 'private_key_bits' => 4096,
+ 'encrypt_key' => false )
+ );
- openssl_pkey_export($res, $private_key);
- $pubkey = openssl_pkey_get_details($res);
- $public_key = $pubkey["key"];
+ $private_key = '';
- // Save the private key. Send them the public key.
+ openssl_pkey_export($res, $private_key);
- $r = q("UPDATE `contact` SET `prvkey` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1",
- dbesc($private_key),
- intval($contact_id),
- intval($uid)
- );
+ $pubkey = openssl_pkey_get_details($res);
+ $public_key = $pubkey["key"];
- $params = array();
+ // Save the private key. Send them the public key.
- // Per the protocol document, we will verify both ends by encrypting the dfrn_id with our
- // site private key (person on the other end can decrypt it with our site public key).
- // Then encrypt our profile URL with the other person's site public key. They can decrypt
- // it with their site private key. If the decryption on the other end fails for either
- // item, it indicates tampering or key failure on at least one site and we will not be
- // able to provide a secure communication pathway.
+ $r = q("UPDATE `contact` SET `prvkey` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ dbesc($private_key),
+ intval($contact_id),
+ intval($uid)
+ );
- // If other site is willing to accept full encryption, (aes_allow is 1 AND we have php5.3
- // or later) then we encrypt the personal public key we send them using AES-256-CBC and a
- // random key which is encrypted with their site public key.
+ $params = array();
- $src_aes_key = random_string();
+ // Per the protocol document, we will verify both ends by encrypting the dfrn_id with our
+ // site private key (person on the other end can decrypt it with our site public key).
+ // Then encrypt our profile URL with the other person's site public key. They can decrypt
+ // it with their site private key. If the decryption on the other end fails for either
+ // item, it indicates tampering or key failure on at least one site and we will not be
+ // able to provide a secure communication pathway.
- $result = '';
- openssl_private_encrypt($dfrn_id,$result,$user[0]['prvkey']);
+ // If other site is willing to accept full encryption, (aes_allow is 1 AND we have php5.3
+ // or later) then we encrypt the personal public key we send them using AES-256-CBC and a
+ // random key which is encrypted with their site public key.
- $params['dfrn_id'] = bin2hex($result);
- $params['public_key'] = $public_key;
+ $src_aes_key = random_string();
+ $result = '';
+ openssl_private_encrypt($dfrn_id,$result,$user[0]['prvkey']);
- $my_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
+ $params['dfrn_id'] = bin2hex($result);
+ $params['public_key'] = $public_key;
- openssl_public_encrypt($my_url, $params['source_url'], $site_pubkey);
- $params['source_url'] = bin2hex($params['source_url']);
- if($aes_allow && function_exists('openssl_encrypt')) {
- openssl_public_encrypt($src_aes_key, $params['aes_key'], $site_pubkey);
- $params['aes_key'] = bin2hex($params['aes_key']);
- $params['public_key'] = bin2hex(openssl_encrypt($public_key,'AES-256-CBC',$src_aes_key));
- }
+ $my_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
- $params['dfrn_version'] = DFRN_PROTOCOL_VERSION ;
- if($duplex == 1)
- $params['duplex'] = 1;
+ openssl_public_encrypt($my_url, $params['source_url'], $site_pubkey);
+ $params['source_url'] = bin2hex($params['source_url']);
- // POST all this stuff to the other site.
+ if($aes_allow && function_exists('openssl_encrypt')) {
+ openssl_public_encrypt($src_aes_key, $params['aes_key'], $site_pubkey);
+ $params['aes_key'] = bin2hex($params['aes_key']);
+ $params['public_key'] = bin2hex(openssl_encrypt($public_key,'AES-256-CBC',$src_aes_key));
+ }
- $res = post_url($dfrn_confirm,$params);
+ $params['dfrn_version'] = DFRN_PROTOCOL_VERSION ;
+ if($duplex == 1)
+ $params['duplex'] = 1;
- // Now figure out what they responded. Try to be robust if the remote site is
- // having difficulty and throwing up errors of some kind.
+ // POST all this stuff to the other site.
- $leading_junk = substr($res,0,strpos($res,'<?xml'));
+ $res = post_url($dfrn_confirm,$params);
- $res = substr($res,strpos($res,'<?xml'));
- if(! strlen($res)) {
+ // Now figure out what they responded. Try to be robust if the remote site is
+ // having difficulty and throwing up errors of some kind.
- // No XML at all, this exchange is messed up really bad.
- // We shouldn't proceed, because the xml parser might choke,
- // and $status is going to be zero, which indicates success.
- // We can hardly call this a success.
+ $leading_junk = substr($res,0,strpos($res,'<?xml'));
- notice( t('Response from remote site was not understood.') . EOL);
- return;
- }
+ $res = substr($res,strpos($res,'<?xml'));
+ if(! strlen($res)) {
- if(strlen($leading_junk) && get_config('system','debugging')) {
+ // No XML at all, this exchange is messed up really bad.
+ // We shouldn't proceed, because the xml parser might choke,
+ // and $status is going to be zero, which indicates success.
+ // We can hardly call this a success.
+
+ notice( t('Response from remote site was not understood.') . EOL);
+ return;
+ }
- // This might be more common. Mixed error text and some XML.
- // If we're configured for debugging, show the text. Proceed in either case.
+ if(strlen($leading_junk) && get_config('system','debugging')) {
+
+ // This might be more common. Mixed error text and some XML.
+ // If we're configured for debugging, show the text. Proceed in either case.
- notice( t('Unexpected response from remote site: ') . EOL . $leading_junk . EOL );
- }
+ notice( t('Unexpected response from remote site: ') . EOL . $leading_junk . EOL );
+ }
- $xml = simplexml_load_string($res);
- $status = (int) $xml->status;
- $message = unxmlify($xml->message); // human readable text of what may have gone wrong.
- switch($status) {
- case 0:
- notice( t("Confirmation completed successfully.") . EOL);
- if(strlen($message))
- notice( t('Remote site reported: ') . $message . EOL);
- break;
- case 1:
- // birthday paradox - generate new dfrn-id and fall through.
- $new_dfrn_id = random_string();
- $r = q("UPDATE contact SET `issued-id` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1",
- dbesc($new_dfrn_id),
- intval($contact_id),
- intval($uid)
+ $xml = simplexml_load_string($res);
+ $status = (int) $xml->status;
+ $message = unxmlify($xml->message); // human readable text of what may have gone wrong.
+ switch($status) {
+ case 0:
+ notice( t("Confirmation completed successfully.") . EOL);
+ if(strlen($message))
+ notice( t('Remote site reported: ') . $message . EOL);
+ break;
+ case 1:
+ // birthday paradox - generate new dfrn-id and fall through.
+ $new_dfrn_id = random_string();
+ $r = q("UPDATE contact SET `issued-id` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ dbesc($new_dfrn_id),
+ intval($contact_id),
+ intval($uid)
+ );
+
+ case 2:
+ notice( t("Temporary failure. Please wait and try again.") . EOL);
+ if(strlen($message))
+ notice( t('Remote site reported: ') . $message . EOL);
+ break;
+
+
+ case 3:
+ notice( t("Introduction failed or was revoked.") . EOL);
+ if(strlen($message))
+ notice( t('Remote site reported: ') . $message . EOL);
+ break;
+ }
+
+ if(($status == 0) && ($intro_id)) {
+
+ // Success. Delete the notification.
+
+ $r = q("DELETE FROM `intro` WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ intval($intro_id),
+ intval($uid)
);
-
- case 2:
- notice( t("Temporary failure. Please wait and try again.") . EOL);
- if(strlen($message))
- notice( t('Remote site reported: ') . $message . EOL);
- break;
-
-
- case 3:
- notice( t("Introduction failed or was revoked.") . EOL);
- if(strlen($message))
- notice( t('Remote site reported: ') . $message . EOL);
- break;
+
}
- if(($status == 0) && ($intro_id)) {
-
- // Success. Delete the notification.
-
- $r = q("DELETE FROM `intro` WHERE `id` = %d AND `uid` = %d LIMIT 1",
- intval($intro_id),
- intval($uid)
- );
-
+ if($status != 0)
+ return;
}
- if($status != 0)
- return;
-
// We have now established a relationship with the other site.
// Let's make our own personal copy of their profile photo so we don't have
// to always load it from their site.
@@ -223,36 +231,29 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
$photo_failure = false;
- $r = q("SELECT `photo` FROM `contact` WHERE `id` = %d LIMIT 1",
- intval($contact_id));
- if(count($r)) {
-
- $filename = basename($r[0]['photo']);
- $img_str = fetch_url($r[0]['photo'],true);
- $img = new Photo($img_str);
- if($img->is_valid()) {
+ $filename = basename($contact['photo']);
+ $img_str = fetch_url($contact['photo'],true);
+ $img = new Photo($img_str);
+ if($img->is_valid()) {
- $img->scaleImageSquare(175);
+ $img->scaleImageSquare(175);
- $hash = photo_new_resource();
+ $hash = photo_new_resource();
- $r = $img->store($uid, $contact_id, $hash, $filename, t('Contact Photos'), 4 );
+ $r = $img->store($uid, $contact_id, $hash, $filename, t('Contact Photos'), 4 );
- if($r === false)
- $photo_failure = true;
-
- $img->scaleImage(80);
+ if($r === false)
+ $photo_failure = true;
- $r = $img->store($uid, $contact_id, $hash, $filename, t('Contact Photos'), 5 );
+ $img->scaleImage(80);
- if($r === false)
- $photo_failure = true;
+ $r = $img->store($uid, $contact_id, $hash, $filename, t('Contact Photos'), 5 );
- $photo = $a->get_baseurl() . '/photo/' . $hash . '-4.jpg';
- $thumb = $a->get_baseurl() . '/photo/' . $hash . '-5.jpg';
- }
- else
+ if($r === false)
$photo_failure = true;
+
+ $photo = $a->get_baseurl() . '/photo/' . $hash . '-4.jpg';
+ $thumb = $a->get_baseurl() . '/photo/' . $hash . '-5.jpg';
}
else
$photo_failure = true;
@@ -262,40 +263,89 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
$thumb = $a->get_baseurl() . '/images/default-profile-sm.jpg';
}
- $new_relation = REL_VIP;
- if(($relation == REL_FAN) || ($duplex))
- $new_relation = REL_BUD;
- $r = q("UPDATE `contact` SET `photo` = '%s',
- `thumb` = '%s',
- `rel` = %d,
- `name-date` = '%s',
- `uri-date` = '%s',
- `avatar-date` = '%s',
- `blocked` = 0,
- `pending` = 0,
- `duplex` = %d,
- `network` = 'dfrn' WHERE `id` = %d LIMIT 1
- ",
- dbesc($photo),
- dbesc($thumb),
- intval($new_relation),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- intval($duplex),
- intval($contact_id)
- );
+ if($contact['network'] === 'dfrn') {
+
+ $new_relation = REL_VIP;
+ if(($relation == REL_FAN) || ($duplex))
+ $new_relation = REL_BUD;
+
+ $r = q("UPDATE `contact` SET `photo` = '%s',
+ `thumb` = '%s',
+ `rel` = %d,
+ `name-date` = '%s',
+ `uri-date` = '%s',
+ `avatar-date` = '%s',
+ `blocked` = 0,
+ `pending` = 0,
+ `duplex` = %d,
+ `network` = 'dfrn' WHERE `id` = %d LIMIT 1
+ ",
+ dbesc($photo),
+ dbesc($thumb),
+ intval($new_relation),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ intval($duplex),
+ intval($contact_id)
+ );
+ }
+ else {
+
+ $notify = '';
+ $poll = '';
+
+ // $contact['network'] !== 'dfrn'
+
+ $arr = lrdd($contact['url']);
+ if(count($arr)) {
+ foreach($arr as $link) {
+ if($link['@attributes']['rel'] === 'salmon')
+ $notify = $link['@attributes']['href'];
+ if($link['@attributes']['rel'] === NAMESPACE_FEED)
+ $poll = $link['@attributes']['href'];
+ }
+ }
+
+ $r = q("DELETE FROM `intro` WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ intval($intro_id),
+ intval($uid)
+ );
+
+ $r = q("UPDATE `contact` SET `photo` = '%s',
+ `thumb` = '%s',
+ `name-date` = '%s',
+ `uri-date` = '%s',
+ `avatar-date` = '%s',
+ `notify` = '%s',
+ `poll` = '%s',
+ `blocked` = 0,
+ `pending` = 0
+ WHERE `id` = %d LIMIT 1
+ ",
+ dbesc($photo),
+ dbesc($thumb),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc($notify),
+ dbesc($poll),
+ intval($contact_id)
+ );
+ }
+
if($r === false)
- notice( t('Unable to set contact photo.') . EOL);
+ notice( t('Unable to set contact photo.') . EOL);
// Let's send our user to the contact editor in case they want to
// do anything special with this new friend.
-
+
if($handsfree === null)
goaway($a->get_baseurl() . '/contacts/' . intval($contact_id));
return; //NOTREACHED
+
}
@@ -480,7 +530,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) {
intval($duplex),
intval($dfrn_record)
);
- if($r === false) { // indicates schema is messed up or total db failure
+ if($r === false) { // indicates schema is messed up or total db failure
$message = t('Unable to update your contact profile details on our system');
xml_status(3,$message);
}
diff --git a/mod/dfrn_notify.php b/mod/dfrn_notify.php
index 517c91eda..fc609ddc0 100644
--- a/mod/dfrn_notify.php
+++ b/mod/dfrn_notify.php
@@ -205,7 +205,7 @@ function dfrn_notify_post(&$a) {
if($is_reply) {
if($feed->get_item_quantity() == 1) {
// remote reply to our post. Import and then notify everybody else.
- $datarray = get_atom_elements($item);
+ $datarray = get_atom_elements($feed,$item);
$datarray['type'] = 'remote-comment';
$datarray['wall'] = 1;
$datarray['parent-uri'] = $parent_uri;
@@ -287,7 +287,7 @@ function dfrn_notify_post(&$a) {
}
continue;
}
- $datarray = get_atom_elements($item);
+ $datarray = get_atom_elements($feed,$item);
$datarray['parent-uri'] = $parent_uri;
$datarray['uid'] = $importer['importer_uid'];
$datarray['contact-id'] = $importer['id'];
@@ -354,7 +354,7 @@ function dfrn_notify_post(&$a) {
}
- $datarray = get_atom_elements($item);
+ $datarray = get_atom_elements($feed,$item);
$datarray['parent-uri'] = $item_id;
$datarray['uid'] = $importer['importer_uid'];
$datarray['contact-id'] = $importer['id'];
diff --git a/mod/network.php b/mod/network.php
index 97a82a705..5958a5905 100644
--- a/mod/network.php
+++ b/mod/network.php
@@ -200,7 +200,7 @@ function network_content(&$a, $update = 0) {
$template = $wallwall;
$commentww = 'ww';
}
- if($item['type'] === 'remote' && ($item['owner-link'] != $item['author-link'])) {
+ if(($item['type'] === 'remote') && (strlen($item['owner-link'])) && ($item['owner-link'] != $item['author-link'])) {
// Could be anybody.
$owner_url = $item['owner-link'];
$owner_photo = $item['owner-avatar'];
diff --git a/mod/notifications.php b/mod/notifications.php
index d09ec5ede..b4116bbe3 100644
--- a/mod/notifications.php
+++ b/mod/notifications.php
@@ -71,7 +71,7 @@ function notifications_content(&$a) {
'$hide_text' => ((strlen($sql_extra)) ? t('Show Ignored Requests') : t('Hide Ignored Requests'))
));
- $r = q("SELECT `intro`.`id` AS `intro-id`, `intro`.*, `contact`.*
+ $r = q("SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*
FROM `intro` LEFT JOIN `contact` ON `intro`.`contact-id` = `contact`.`id`
WHERE `intro`.`uid` = %d $sql_extra AND `intro`.`blocked` = 0 ",
intval($_SESSION['uid']));
@@ -83,15 +83,36 @@ function notifications_content(&$a) {
foreach($r as $rr) {
+ $friend_selected = (($rr['network'] === 'dfrn') ? ' checked="checked" ' : ' disabled ');
+ $fan_selected = (($rr['network'] === 'stat') ? ' checked="checked" disabled ' : '');
+ $dfrn_tpl = load_view_file('view/netfriend.tpl');
+
+ $knowyou = '';
+ $dfrn_text = '';
+
+ if($rr['network'] === 'dfrn') {
+ $knowyou = t('Claims to be known to you: ') . (($rr['knowyou']) ? t('yes') : t('no'));
+
+ $dfrn_text = replace_macros($dfrn_tpl,array(
+ '$intro_id' => $rr['intro_id'],
+ '$friend_selected' => $friend_selected,
+ '$fan_selected' => $fan_selected,
+ ));
+ }
+
+
+
$o .= replace_macros($tpl,array(
- '$intro_id' => $rr['intro-id'],
- '$dfrn-id' => $rr['issued-id'],
+ '$notify_type' => (($rr['network'] === 'dfrn') ? t('Friend/Connect Request') : t('New Follower')),
+ '$dfrn_text' => $dfrn_text,
+ '$dfrn_id' => $rr['issued-id'],
'$uid' => $_SESSION['uid'],
- '$contact-id' => $rr['contact-id'],
+ '$intro_id' => $rr['intro_id'],
+ '$contact_id' => $rr['contact-id'],
'$photo' => ((x($rr,'photo')) ? $rr['photo'] : "images/default-profile.jpg"),
'$fullname' => $rr['name'],
- '$knowyou' => (($rr['knowyou']) ? t('yes') : t('no')),
'$url' => $rr['url'],
+ '$knowyou' => $knowyou,
'$note' => $rr['note']
));
}
diff --git a/mod/salmon.php b/mod/salmon.php
index 30e87f243..74377f9e9 100644
--- a/mod/salmon.php
+++ b/mod/salmon.php
@@ -195,14 +195,16 @@ function salmon_post(&$a) {
*
*/
- $r = q("SELECT * FROM `contact` WHERE `network` = 'stat' AND `lrdd` = '%s' AND `uid` = %d LIMIT 1",
+ $r = q("SELECT * FROM `contact` WHERE `network` = 'stat' AND ( `url` = '%s' OR `lrdd` = '%s') AND `uid` = %d
+ AND `readonly` = 0 LIMIT 1",
+ dbesc($author_link),
dbesc($author_link),
intval($importer['uid'])
);
if(! count($r)) {
if($debugging)
file_put_contents('salmon.out',"\n" . 'Author unknown to us.' . "\n", FILE_APPEND);
- salmon_return(500);
+
}
require_once('include/items.php');
@@ -212,7 +214,9 @@ function salmon_post(&$a) {
$hub = '';
- consume_feed($feedxml,$importer,$r[0],$hub);
+ // consume_feed will only accept a follow activity from this person if there is no contact record.
+
+ consume_feed($feedxml,$importer,((count($r)) ? $r[0] : null),$hub);
salmon_return(200);
}
diff --git a/view/acl_selectors.php b/view/acl_selectors.php
index 7ad97dfd2..b5f2dcfea 100644
--- a/view/acl_selectors.php
+++ b/view/acl_selectors.php
@@ -50,7 +50,7 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p
$r = q("SELECT `id`, `name`, `url` FROM `contact`
- WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0
+ WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `network` = 'dfrn'
$sql_extra
ORDER BY `name` ASC ",
$_SESSION['uid']
diff --git a/view/intros.tpl b/view/intros.tpl
index a56c7742d..30198beb8 100644
--- a/view/intros.tpl
+++ b/view/intros.tpl
@@ -1,12 +1,12 @@
-<div class="intro-wrapper" id="intro-$contact-id" >
+<div class="intro-wrapper" id="intro-$contact_id" >
-<p class="intro-desc">Notification type: Friend/Connect Request</p>
-<div class="intro-fullname" id="intro-fullname-$contact-id" >$fullname</div>
-<a class="intro-url-link" id="intro-url-link-$contact-id" href="$url" ><img id="photo-$contact-id" class="intro-photo" src="$photo" width="175" height=175" name="$fullname" alt="fullname" /></a>
-<div class="intro-knowyou">Presumably known to you? <strong>$knowyou</strong></div>
-<div class="intro-note" id="intro-note-$contact-id">$note</div>
-<div class="intro-wrapper-end" id="intro-wrapper-end-$contact-id"></div>
+<p class="intro-desc">Notification type: $notify_type</p>
+<div class="intro-fullname" id="intro-fullname-$contact_id" >$fullname</div>
+<a class="intro-url-link" id="intro-url-link-$contact_id" href="$url" ><img id="photo-$contact_id" class="intro-photo" src="$photo" width="175" height=175" name="$fullname" alt="fullname" /></a>
+<div class="intro-knowyou">$knowyou</div>
+<div class="intro-note" id="intro-note-$contact_id">$note</div>
+<div class="intro-wrapper-end" id="intro-wrapper-end-$contact_id"></div>
<form class="intro-form" action="notifications/$intro_id" method="post">
<input class="intro-submit-ignore" type="submit" name="submit" value="Ignore" />
<input class="intro-submit-discard" type="submit" name="submit" value="Discard" />
@@ -14,23 +14,11 @@
<div class="intro-form-end"></div>
<form class="intro-approve-form" action="dfrn_confirm" method="post">
-<input type="hidden" name="dfrn_id" value="$dfrn-id" >
+<input type="hidden" name="dfrn_id" value="$dfrn_id" >
<input type="hidden" name="intro_id" value="$intro_id" >
+<input type="hidden" name="contact_id" value="$contact_id" >
-<div class="intro-approve-as-friend-desc">Approve as: </div>
-
-<div class="intro-approve-as-friend-wrapper">
- <label class="intro-approve-as-friend-label" for="intro-approve-as-friend-$intro_id">Friend</label>
- <input type="radio" name="duplex" id="intro-approve-as-friend-$intro_id" class="intro-approve-as-friend" checked="checked" value="1" />
- <div class="intro-approve-friend-break" ></div>
-</div>
-<div class="intro-approve-as-friend-end"></div>
-<div class="intro-approve-as-fan-wrapper">
- <label class="intro-approve-as-fan-label" for="intro-approve-as-fan-$intro_id">Fan/Admirer</label>
- <input type="radio" name="duplex" id="intro-approve-as-fan-$intro_id" class="intro-approve-as-fan" $fan_selected value="0" />
- <div class="intro-approve-fan-break"></div>
-</div>
-<div class="intro-approve-as-end"></div>
+$dfrn_text
<input class="intro-submit-approve" type="submit" name="submit" value="Approve" />
</form>
diff --git a/view/magicsig.tpl b/view/magicsig.tpl
new file mode 100644
index 000000000..46b4d4ea4
--- /dev/null
+++ b/view/magicsig.tpl
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<me:env xmlns:me='http://salmon-protocol.org/ns/magic-env'>
+<me:data type='application/atom+xml'>
+$data
+</me:data>
+<me:encoding>$encoding</me:encoding>
+<me:alg>$algorithm</me:/alg>
+<me:sig keyash="$keyhash">$signature</me:sig>
+</me:env>
diff --git a/view/netfriend.tpl b/view/netfriend.tpl
new file mode 100644
index 000000000..b041ec49f
--- /dev/null
+++ b/view/netfriend.tpl
@@ -0,0 +1,14 @@
+<div class="intro-approve-as-friend-desc">Approve as: </div>
+
+<div class="intro-approve-as-friend-wrapper">
+ <label class="intro-approve-as-friend-label" for="intro-approve-as-friend-$intro_id">Friend</label>
+ <input type="radio" name="duplex" id="intro-approve-as-friend-$intro_id" class="intro-approve-as-friend" $friend_selected value="1" />
+ <div class="intro-approve-friend-break" ></div>
+</div>
+<div class="intro-approve-as-friend-end"></div>
+<div class="intro-approve-as-fan-wrapper">
+ <label class="intro-approve-as-fan-label" for="intro-approve-as-fan-$intro_id">Fan/Admirer</label>
+ <input type="radio" name="duplex" id="intro-approve-as-fan-$intro_id" class="intro-approve-as-fan" $fan_selected value="0" />
+ <div class="intro-approve-fan-break"></div>
+</div>
+<div class="intro-approve-as-end"></div>
diff --git a/view/theme/default/style.css b/view/theme/default/style.css
index 32d8eeba0..ca4891a76 100644
--- a/view/theme/default/style.css
+++ b/view/theme/default/style.css
@@ -527,6 +527,10 @@ input#dfrn-url {
clear: both;
}
+.intro-wrapper {
+ margin-top: 20px;
+}
+
.intro-fullname {
font-size: 1.1em;
font-weight: bold;
@@ -534,6 +538,7 @@ input#dfrn-url {
}
.intro-desc {
margin-bottom: 20px;
+ font-weight: bold;
}
.intro-note {
@@ -556,6 +561,10 @@ input#dfrn-url {
.intro-submit-approve, .intro-submit-ignore {
margin-right: 20px;
}
+.intro-submit-approve {
+ margin-top: 15px;
+}
+
.intro-approve-as-friend-label, .intro-approve-as-fan-label {
float: left;
width: 100px;