Merge branch 'master' of https://github.com/redmatrix/hubzilla into contextual-help
This commit is contained in:
@@ -73,16 +73,18 @@ function contact_poll_interval($current, $disabled = false) {
|
||||
function network_to_name($s) {
|
||||
|
||||
$nets = array(
|
||||
NETWORK_DFRN => t('Friendica'),
|
||||
NETWORK_OSTATUS => t('OStatus'),
|
||||
NETWORK_FEED => t('RSS/Atom'),
|
||||
NETWORK_MAIL => t('Email'),
|
||||
NETWORK_DIASPORA => t('Diaspora'),
|
||||
NETWORK_FACEBOOK => t('Facebook'),
|
||||
NETWORK_ZOT => t('Zot'),
|
||||
NETWORK_LINKEDIN => t('LinkedIn'),
|
||||
NETWORK_XMPP => t('XMPP/IM'),
|
||||
NETWORK_MYSPACE => t('MySpace'),
|
||||
NETWORK_DFRN => t('Friendica'),
|
||||
NETWORK_FRND => t('Friendica'),
|
||||
NETWORK_OSTATUS => t('OStatus'),
|
||||
NETWORK_GNUSOCIAL => t('GNU-Social'),
|
||||
NETWORK_FEED => t('RSS/Atom'),
|
||||
NETWORK_MAIL => t('Email'),
|
||||
NETWORK_DIASPORA => t('Diaspora'),
|
||||
NETWORK_FACEBOOK => t('Facebook'),
|
||||
NETWORK_ZOT => t('Zot'),
|
||||
NETWORK_LINKEDIN => t('LinkedIn'),
|
||||
NETWORK_XMPP => t('XMPP/IM'),
|
||||
NETWORK_MYSPACE => t('MySpace'),
|
||||
);
|
||||
|
||||
call_hooks('network_to_name', $nets);
|
||||
|
||||
@@ -308,11 +308,33 @@ function metorsa($m,$e) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function salmon_key($pubkey) {
|
||||
pemtome($pubkey,$m,$e);
|
||||
return 'RSA' . '.' . base64url_encode($m,true) . '.' . base64url_encode($e,true) ;
|
||||
}
|
||||
|
||||
|
||||
function convert_salmon_key($key) {
|
||||
|
||||
if(strstr($key,','))
|
||||
$rawkey = substr($key,strpos($key,',')+1);
|
||||
else
|
||||
$rawkey = substr($key,5);
|
||||
|
||||
$key_info = explode('.',$rawkey);
|
||||
|
||||
$m = base64url_decode($key_info[1]);
|
||||
$e = base64url_decode($key_info[2]);
|
||||
|
||||
logger('key details: ' . print_r($key_info,true), LOGGER_DATA);
|
||||
$salmon_key = metopem($m,$e);
|
||||
return $salmon_key;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function z_obscure($s) {
|
||||
return json_encode(crypto_encapsulate($s,get_config('system','pubkey')));
|
||||
}
|
||||
@@ -322,3 +344,4 @@ function z_unobscure($s) {
|
||||
return $s;
|
||||
return crypto_unencapsulate(json_decode($s,true),get_config('system','prvkey'));
|
||||
}
|
||||
|
||||
|
||||
@@ -134,26 +134,29 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
$their_perms = 0;
|
||||
$xchan_hash = '';
|
||||
|
||||
|
||||
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1",
|
||||
dbesc($url),
|
||||
dbesc($url)
|
||||
);
|
||||
|
||||
|
||||
if(! $r) {
|
||||
// attempt network auto-discovery
|
||||
if(strpos($url,'@') && (! $is_http)) {
|
||||
$d = discover_by_webbie($url);
|
||||
}
|
||||
elseif($is_http) {
|
||||
if(get_config('system','feed_contacts'))
|
||||
|
||||
$d = discover_by_webbie($url);
|
||||
|
||||
if((! $d) && ($is_http)) {
|
||||
|
||||
// try RSS discovery
|
||||
|
||||
if(get_config('system','feed_contacts')) {
|
||||
$d = discover_by_url($url);
|
||||
}
|
||||
else {
|
||||
$result['message'] = t('Protocol disabled.');
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if($d) {
|
||||
$r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1",
|
||||
dbesc($url),
|
||||
@@ -161,6 +164,9 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if discovery was a success we should have an xchan record in $r
|
||||
|
||||
if($r) {
|
||||
$xchan = $r[0];
|
||||
$xchan_hash = $r[0]['xchan_hash'];
|
||||
@@ -187,28 +193,13 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
}
|
||||
$singleton = intval($x['singleton']);
|
||||
|
||||
if((local_channel()) && $uid == local_channel()) {
|
||||
$aid = get_account_id();
|
||||
$hash = get_observer_hash();
|
||||
$ch = $a->get_channel();
|
||||
$default_group = $ch['channel_default_group'];
|
||||
}
|
||||
else {
|
||||
$r = q("select * from channel where channel_id = %d limit 1",
|
||||
intval($uid)
|
||||
);
|
||||
if(! $r) {
|
||||
$result['message'] = t('local account not found.');
|
||||
return $result;
|
||||
}
|
||||
$aid = $r[0]['channel_account_id'];
|
||||
$hash = $r[0]['channel_hash'];
|
||||
$default_group = $r[0]['channel_default_group'];
|
||||
}
|
||||
$aid = $channel['channel_account_id'];
|
||||
$hash = get_observer_hash();
|
||||
$default_group = $channel['channel_default_group'];
|
||||
|
||||
if($xchan['xchan_network'] === 'rss') {
|
||||
|
||||
if($is_http) {
|
||||
|
||||
// check service class feed limits
|
||||
|
||||
$r = q("select count(*) as total from abook where abook_account = %d and abook_feed = 1 ",
|
||||
intval($aid)
|
||||
@@ -232,7 +223,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
|
||||
if($r) {
|
||||
$abook_instance = $r[0]['abook_instance'];
|
||||
|
||||
@@ -282,7 +272,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
|
||||
proc_run('php', 'include/notifier.php', 'permission_create', $result['abook']['abook_id']);
|
||||
}
|
||||
|
||||
$arr = array('channel_id' => $uid, 'abook' => $result['abook']);
|
||||
$arr = array('channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook']);
|
||||
|
||||
call_hooks('follow', $arr);
|
||||
|
||||
|
||||
@@ -211,6 +211,22 @@ function group_get_members($gid) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function group_get_members_xchan($gid) {
|
||||
$ret = array();
|
||||
if(intval($gid)) {
|
||||
$r = q("SELECT xchan FROM group_member WHERE gid = %d AND uid = %d",
|
||||
intval($gid),
|
||||
intval(local_channel())
|
||||
);
|
||||
if(count($r)) {
|
||||
foreach($r as $rr) {
|
||||
$ret[] = $rr['xchan'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function mini_group_select($uid,$group = '') {
|
||||
|
||||
$grps = array();
|
||||
|
||||
@@ -4214,7 +4214,7 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
|
||||
|
||||
if(($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) {
|
||||
$parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']);
|
||||
$o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n";
|
||||
$o .= '<thr:in-reply-to ref="' . z_root() . '/display/' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n";
|
||||
}
|
||||
|
||||
if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) {
|
||||
@@ -4232,7 +4232,7 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
|
||||
$o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n";
|
||||
}
|
||||
|
||||
$o .= '<id>' . xmlify($item['mid']) . '</id>' . "\r\n";
|
||||
$o .= '<id>' . z_root() . '/display/' . xmlify($item['mid']) . '</id>' . "\r\n";
|
||||
$o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n";
|
||||
$o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n";
|
||||
|
||||
|
||||
@@ -185,9 +185,11 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) {
|
||||
if($ciphers)
|
||||
@curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers);
|
||||
|
||||
if(x($opts,'headers'))
|
||||
if(x($opts,'headers')) {
|
||||
@curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']);
|
||||
|
||||
logger('headers: ' . print_r($opts['headers'],true) . 'redir: ' . $redirects);
|
||||
}
|
||||
|
||||
if(x($opts,'nobody'))
|
||||
@curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']);
|
||||
|
||||
@@ -236,6 +238,21 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) {
|
||||
$base = substr($base,strlen($chunk));
|
||||
}
|
||||
|
||||
// would somebody take lighttpd and just shoot it?
|
||||
|
||||
if($http_code == 417) {
|
||||
curl_close($ch);
|
||||
if($opts) {
|
||||
if($opts['headers'])
|
||||
$opts['headers'][] = 'Expect:';
|
||||
else
|
||||
$opts['headers'] = array('Expect:');
|
||||
}
|
||||
else
|
||||
$opts = array('headers' => array('Expect:'));
|
||||
return z_post_url($url,$params,++$redirects,$opts);
|
||||
}
|
||||
|
||||
if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) {
|
||||
$matches = array();
|
||||
preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
|
||||
@@ -1044,32 +1061,16 @@ function discover_by_url($url,$arr = null) {
|
||||
}
|
||||
|
||||
|
||||
function convert_salmon_key($key) {
|
||||
|
||||
if(strstr($key,','))
|
||||
$rawkey = substr($key,strpos($key,',')+1);
|
||||
else
|
||||
$rawkey = substr($key,5);
|
||||
|
||||
$key_info = explode('.',$rawkey);
|
||||
|
||||
$m = base64url_decode($key_info[1]);
|
||||
$e = base64url_decode($key_info[2]);
|
||||
|
||||
logger('key details: ' . print_r($key_info,true), LOGGER_DEBUG);
|
||||
$salmon_key = metopem($m,$e);
|
||||
return $salmon_key;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function discover_by_webbie($webbie) {
|
||||
require_once('library/HTML5/Parser.php');
|
||||
|
||||
$result = array();
|
||||
$network = null;
|
||||
$result = array();
|
||||
|
||||
$network = null;
|
||||
|
||||
$diaspora = false;
|
||||
$gnusoc = false;
|
||||
$gnusoc = false;
|
||||
$dfrn = false;
|
||||
|
||||
$has_salmon = false;
|
||||
$salmon_key = false;
|
||||
@@ -1077,7 +1078,6 @@ function discover_by_webbie($webbie) {
|
||||
$diaspora_base = '';
|
||||
$diaspora_guid = '';
|
||||
$diaspora_key = '';
|
||||
$dfrn = false;
|
||||
|
||||
$webbie = strtolower($webbie);
|
||||
|
||||
@@ -1085,6 +1085,10 @@ function discover_by_webbie($webbie) {
|
||||
if($x && array_key_exists('links',$x) && $x['links']) {
|
||||
foreach($x['links'] as $link) {
|
||||
if(array_key_exists('rel',$link)) {
|
||||
|
||||
// If we discover zot - don't search further; grab the info and get out of
|
||||
// here.
|
||||
|
||||
if($link['rel'] == 'http://purl.org/zot/protocol') {
|
||||
logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG);
|
||||
if(array_key_exists('zot',$x) && $x['zot']['success'])
|
||||
@@ -1098,6 +1102,9 @@ function discover_by_webbie($webbie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if($link['rel'] == NAMESPACE_DFRN) {
|
||||
$dfrn = $link['href'];
|
||||
}
|
||||
if($link['rel'] == 'magic-public-key') {
|
||||
if(substr($link['href'],0,5) === 'data:') {
|
||||
$salmon_key = convert_salmon_key($link['href']);
|
||||
@@ -1105,6 +1112,7 @@ function discover_by_webbie($webbie) {
|
||||
}
|
||||
if($link['rel'] == 'salmon') {
|
||||
$has_salmon = true;
|
||||
$salmon = $link['href'];
|
||||
}
|
||||
if($link['rel'] == 'http://schemas.google.com/g/2010#updates-from') {
|
||||
$atom_feed = $link['href'];
|
||||
@@ -1113,82 +1121,40 @@ function discover_by_webbie($webbie) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
logger('webfing: ' . print_r($x,true));
|
||||
logger('webfinger: ' . print_r($x,true), LOGGER_DATA, LOG_INFO);
|
||||
|
||||
$arr = array('address' => $webbie, 'success' => false, 'webfinger' => $x);
|
||||
call_hooks('discover_channel_webfinger', $arr);
|
||||
if($arr['success'])
|
||||
return true;
|
||||
|
||||
if($salmon_key && $has_salmon && $atom_feed) {
|
||||
|
||||
$gnusoc = true;
|
||||
$addr = $x['address'];
|
||||
$aliases = array();
|
||||
|
||||
$m = parse_url($x['location']);
|
||||
// Now let's make some decisions on what we may need
|
||||
// to obtain further info
|
||||
|
||||
$k = z_fetch_url($atom_feed);
|
||||
if($k['success'])
|
||||
$feed_meta = feed_meta($k['body']);
|
||||
if($feed_meta && $feed_meta['author']) {
|
||||
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($addr)
|
||||
);
|
||||
if($r) {
|
||||
$r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1",
|
||||
dbesc(($feed_meta['author']['author_name']) ? $feed_meta['author']['author_name'] : $x['nickname']),
|
||||
dbesc('gnusoc'),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($addr)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$probe_atom = false;
|
||||
$probe_old = false;
|
||||
$probe_hcard = false;
|
||||
|
||||
$r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ",
|
||||
dbesc($addr),
|
||||
dbesc($x['location']),
|
||||
dbesc($salmon_key),
|
||||
dbesc($addr),
|
||||
dbesc($x['location']),
|
||||
dbesc(($feed_meta['author']['author_name']) ? $feed_meta['author']['author_name'] : $x['nickname']),
|
||||
dbesc('gnusoc'),
|
||||
dbescdate(datetime_convert())
|
||||
);
|
||||
}
|
||||
$address = '';
|
||||
$location = '';
|
||||
$nickname = '';
|
||||
$fullname = '';
|
||||
$avatar = '';
|
||||
$pubkey = '';
|
||||
|
||||
$r = q("select * from hubloc where hubloc_hash = '%s' limit 1",
|
||||
dbesc($addr)
|
||||
);
|
||||
if(array_key_exists('address',$x))
|
||||
$address = $x['address'];
|
||||
if(array_key_exists('location',$x))
|
||||
$location = $x['location'];
|
||||
if(array_key_exists('nickname',$x))
|
||||
$nickname = $x['nickname'];
|
||||
|
||||
if(! $r) {
|
||||
|
||||
$r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)",
|
||||
dbesc($x['location']),
|
||||
dbesc($addr),
|
||||
dbesc($addr),
|
||||
dbesc('gnusoc'),
|
||||
dbesc($m['scheme'] . '://' . $m['host']),
|
||||
dbesc($m['host']),
|
||||
dbesc($salmon),
|
||||
dbescdate(datetime_convert())
|
||||
);
|
||||
}
|
||||
$photos = import_xchan_photo($feed_meta['author']['author_photo'],$addr);
|
||||
$r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
|
||||
dbescdate(datetime_convert()),
|
||||
dbesc($photos[0]),
|
||||
dbesc($photos[1]),
|
||||
dbesc($photos[2]),
|
||||
dbesc($photos[3]),
|
||||
dbesc($addr)
|
||||
);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(! $x)
|
||||
$probe_old = true;
|
||||
|
||||
if($probe_old) {
|
||||
$x = old_webfinger($webbie);
|
||||
if($x) {
|
||||
logger('old_webfinger: ' . print_r($x,true));
|
||||
@@ -1221,174 +1187,195 @@ function discover_by_webbie($webbie) {
|
||||
$pubkey = $diaspora_key;
|
||||
$diaspora = true;
|
||||
}
|
||||
if($link['@attributes']['rel'] == 'magic-public-key') {
|
||||
if(substr($link['@attributes']['href'],0,5) === 'data:') {
|
||||
$salmon_key = convert_salmon_key($link['@attributes']['href']);
|
||||
}
|
||||
}
|
||||
if($link['@attributes']['rel'] == 'salmon') {
|
||||
$has_salmon = true;
|
||||
$salmon = $link['@attributes']['href'];
|
||||
}
|
||||
|
||||
if($link['@attributes']['rel'] == 'http://schemas.google.com/g/2010#updates-from') {
|
||||
$atom_feed = $link['@attributes']['href'];
|
||||
}
|
||||
if($link['@attributes']['rel'] === 'alias') {
|
||||
$aliases[] = $link['@attributes']['href'];
|
||||
}
|
||||
if($link['@attributes']['rel'] === 'subject') {
|
||||
$subject = $link['@attributes']['href'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($diaspora && $diaspora_base && $diaspora_guid) {
|
||||
$guid = $diaspora_guid;
|
||||
$diaspora_base = trim($diaspora_base,'/');
|
||||
|
||||
$notify = $diaspora_base . '/receive';
|
||||
|
||||
if(strpos($webbie,'@')) {
|
||||
$addr = str_replace('acct:', '', $webbie);
|
||||
$hostname = substr($webbie,strpos($webbie,'@')+1);
|
||||
}
|
||||
$network = 'diaspora';
|
||||
// until we get a dfrn layer, we'll use diaspora protocols for Friendica,
|
||||
// but give it a different network so we can go back and fix these when we get proper support.
|
||||
// It really should be just 'friendica' but we also want to distinguish
|
||||
// between Friendica sites that we can use D* protocols with and those we can't.
|
||||
// Some Friendica sites will have Diaspora disabled.
|
||||
if($dfrn)
|
||||
$network = 'friendica-over-diaspora';
|
||||
if($hcard) {
|
||||
$vcard = scrape_vcard($hcard);
|
||||
$vcard['nick'] = substr($webbie,0,strpos($webbie,'@'));
|
||||
if(! $vcard['fn'])
|
||||
$vcard['fn'] = $webbie;
|
||||
}
|
||||
|
||||
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($addr)
|
||||
);
|
||||
|
||||
// fix relative urls
|
||||
if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0))
|
||||
$vcard['photo'] = $diaspora_base . '/' . $vcard['photo'];
|
||||
|
||||
/**
|
||||
*
|
||||
* Diaspora communications are notoriously unreliable and receiving profile update messages (indeed any messages)
|
||||
* are pretty much random luck. We'll check the timestamp of the xchan_name_date at a higher level and refresh
|
||||
* this record once a month; because if you miss a profile update message and they update their profile photo or name
|
||||
* you're otherwise stuck with stale info until they change their profile again - which could be years from now.
|
||||
*
|
||||
*/
|
||||
|
||||
if($r) {
|
||||
$r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1",
|
||||
dbesc($vcard['fn']),
|
||||
dbesc($network),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($addr)
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
$r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ",
|
||||
dbesc($addr),
|
||||
dbesc($guid),
|
||||
dbesc($pubkey),
|
||||
dbesc($addr),
|
||||
dbesc($profile),
|
||||
dbesc($vcard['fn']),
|
||||
dbesc($network),
|
||||
dbescdate(datetime_convert())
|
||||
);
|
||||
}
|
||||
|
||||
$r = q("select * from hubloc where hubloc_hash = '%s' limit 1",
|
||||
dbesc($webbie)
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
|
||||
$r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)",
|
||||
dbesc($guid),
|
||||
dbesc($addr),
|
||||
dbesc($addr),
|
||||
dbesc($network),
|
||||
dbesc(trim($diaspora_base,'/')),
|
||||
dbesc($hostname),
|
||||
dbesc($notify),
|
||||
dbescdate(datetime_convert())
|
||||
);
|
||||
}
|
||||
$photos = import_xchan_photo($vcard['photo'],$addr);
|
||||
$r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
|
||||
dbescdate(datetime_convert()),
|
||||
dbesc($photos[0]),
|
||||
dbesc($photos[1]),
|
||||
dbesc($photos[2]),
|
||||
dbesc($photos[3]),
|
||||
dbesc($addr)
|
||||
);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if($subject || $aliases) {
|
||||
if(strpos($webbie,'@')) {
|
||||
$rhs = substr($webbie,strpos($webbie,'@')+1);
|
||||
}
|
||||
else {
|
||||
$m = parse_url($webbie);
|
||||
if($m) {
|
||||
$rhs = $m['host'] . (($m['port']) ? ':' . $m['port'] : '');
|
||||
}
|
||||
}
|
||||
|
||||
$v = array('subject' => $subject,'aliases' => $aliases);
|
||||
$address = find_webfinger_address($v,$rhs);
|
||||
$location = find_webfinger_location($v,$rhs);
|
||||
if($address)
|
||||
$nickname = substr($address,0,strpos($address,'@'));
|
||||
|
||||
}
|
||||
|
||||
if($salmon_key && $has_salmon && $atom_feed && (! $dfrn) && (! $diaspora)) {
|
||||
$gnusoc = true;
|
||||
$probe_atom = true;
|
||||
}
|
||||
|
||||
if(! $pubkey)
|
||||
$pubkey = $salmon_key;
|
||||
|
||||
if(($dfrn || $diaspora) && $hcard)
|
||||
$probe_hcard = true;
|
||||
|
||||
if(! $fullname)
|
||||
$fullname = $nickname;
|
||||
|
||||
if($probe_atom) {
|
||||
$k = z_fetch_url($atom_feed);
|
||||
if($k['success'])
|
||||
$feed_meta = feed_meta($k['body']);
|
||||
if($feed_meta) {
|
||||
|
||||
// stash any discovered pubsubhubbub hubs in case we need to follow them
|
||||
// this will save an expensive lookup later
|
||||
|
||||
if($feed_meta['hubs'] && $address) {
|
||||
set_xconfig($address,'system','push_hubs',$feed_meta['hubs']);
|
||||
set_xconfig($address,'system','feed_url',$atom_feed);
|
||||
}
|
||||
if($feed_meta['author']['author_name']) {
|
||||
$fullname = $feed_meta['author']['author_name'];
|
||||
}
|
||||
if(! $avatar) {
|
||||
if($feed_meta['author']['author_photo'])
|
||||
$avatar = $feed_meta['author']['author_photo'];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($probe_hcard) {
|
||||
$vcard = scrape_vcard($hcard);
|
||||
if($vcard) {
|
||||
logger('vcard: ' . print_r($vcard,true), LOGGER_DATA);
|
||||
if($vcard['fn'])
|
||||
$fullname = $vcard['fn'];
|
||||
if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0))
|
||||
$vcard['photo'] = $diaspora_base . '/' . $vcard['photo'];
|
||||
if(! $avatar)
|
||||
$avatar = $vcard['photo'];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(($profile) && (! $location))
|
||||
$location = $profile;
|
||||
|
||||
if($location) {
|
||||
$m = parse_url($location);
|
||||
$base = $m['scheme'] . '://' . $m['host'];
|
||||
$host = $m['host'];
|
||||
}
|
||||
|
||||
|
||||
if($diaspora && $diaspora_base && $diaspora_guid) {
|
||||
if($dfrn)
|
||||
$network = 'friendica-over-diaspora';
|
||||
else
|
||||
$network = 'diaspora';
|
||||
|
||||
$base = trim($diaspora_base,'/');
|
||||
$notify = $base . '/receive';
|
||||
|
||||
}
|
||||
else {
|
||||
if($gnusoc) {
|
||||
$network = 'gnusoc';
|
||||
$notify = $salmon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
logger('network: ' . $network);
|
||||
logger('address: ' . $address);
|
||||
logger('fullname: ' . $fullname);
|
||||
logger('pubkey: ' . $pubkey);
|
||||
logger('location: ' . $location);
|
||||
|
||||
|
||||
|
||||
// if we have everything we need, let's create the records
|
||||
|
||||
if($network && $address && $fullname && $pubkey && $location) {
|
||||
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($address)
|
||||
);
|
||||
if($r) {
|
||||
$r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1",
|
||||
dbesc($fullname),
|
||||
dbesc($network),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($address)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ",
|
||||
dbesc($address),
|
||||
dbesc(($diaspora_guid) ? $diaspora_guid : $location),
|
||||
dbesc($pubkey),
|
||||
dbesc($address),
|
||||
dbesc($location),
|
||||
dbesc($fullname),
|
||||
dbesc($network),
|
||||
dbescdate(datetime_convert())
|
||||
);
|
||||
}
|
||||
|
||||
$r = q("select * from hubloc where hubloc_hash = '%s' limit 1",
|
||||
dbesc($address)
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
$r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)",
|
||||
dbesc(($diaspora_guid) ? $diaspora_guid : $location),
|
||||
dbesc($address),
|
||||
dbesc($address),
|
||||
dbesc($network),
|
||||
dbesc($base),
|
||||
dbesc($host),
|
||||
dbesc($notify),
|
||||
dbescdate(datetime_convert())
|
||||
);
|
||||
}
|
||||
$photos = import_xchan_photo($avatar,$address);
|
||||
$r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
|
||||
dbescdate(datetime_convert()),
|
||||
dbesc($photos[0]),
|
||||
dbesc($photos[1]),
|
||||
dbesc($photos[2]),
|
||||
dbesc($photos[3]),
|
||||
dbesc($address)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
/*
|
||||
$vcard['fn'] = notags($vcard['fn']);
|
||||
$vcard['nick'] = str_replace(' ','',notags($vcard['nick']));
|
||||
|
||||
$result['name'] = $vcard['fn'];
|
||||
$result['nick'] = $vcard['nick'];
|
||||
$result['guid'] = $guid;
|
||||
$result['url'] = $profile;
|
||||
$result['hostname'] = $hostname;
|
||||
$result['addr'] = $addr;
|
||||
$result['batch'] = $batch;
|
||||
$result['notify'] = $notify;
|
||||
$result['poll'] = $poll;
|
||||
$result['request'] = $request;
|
||||
$result['confirm'] = $confirm;
|
||||
$result['poco'] = $poco;
|
||||
$result['photo'] = $vcard['photo'];
|
||||
$result['priority'] = $priority;
|
||||
$result['network'] = $network;
|
||||
$result['alias'] = $alias;
|
||||
$result['pubkey'] = $pubkey;
|
||||
|
||||
logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG);
|
||||
|
||||
return $result;
|
||||
|
||||
*/
|
||||
|
||||
/* Sample Diaspora result.
|
||||
|
||||
Array
|
||||
(
|
||||
[name] => Mike Macgirvin
|
||||
[nick] => macgirvin
|
||||
[guid] => a9174a618f8d269a
|
||||
[url] => https://joindiaspora.com/u/macgirvin
|
||||
[hostname] => joindiaspora.com
|
||||
[addr] => macgirvin@joindiaspora.com
|
||||
[batch] =>
|
||||
[notify] => https://joindiaspora.com/receive
|
||||
[poll] => https://joindiaspora.com/public/macgirvin.atom
|
||||
[request] =>
|
||||
[confirm] =>
|
||||
[poco] =>
|
||||
[photo] => https://joindiaspora.s3.amazonaws.com/uploads/images/thumb_large_fec4e6eef13ae5e56207.jpg
|
||||
[priority] =>
|
||||
[network] => diaspora
|
||||
[alias] =>
|
||||
[pubkey] => -----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtihtyIuRDWkDpCA+I1UaQ
|
||||
jI4S7k625+A7EEJm+pL2ZVSJxeCKiFeEgHBQENjLMNNm8l8F6blxgQqE6ZJ9Spa7f
|
||||
tlaXYTRCrfxKzh02L3hR7sNA+JS/nXJaUAIo+IwpIEspmcIRbD9GB7Wv/rr+M28uH
|
||||
31EeYyDz8QL6InU/bJmnCdFvmEMBQxJOw1ih9tQp7UNJAbUMCje0WYFzBz7sfcaHL
|
||||
OyYcCOqOCBLdGucUoJzTQ9iDBVzB8j1r1JkIHoEb2moUoKUp+tkCylNfd/3IVELF9
|
||||
7w1Qjmit3m50OrJk2DQOXvCW9KQxaQNdpRPSwhvemIt98zXSeyZ1q/YjjOwG0DWDq
|
||||
AF8aLj3/oQaZndTPy/6tMiZogKaijoxj8xFLuPYDTw5VpKquriVC0z8oxyRbv4t9v
|
||||
8JZZ9BXqzmayvY3xZGGp8NulrfjW+me2bKh0/df1aHaBwpZdDTXQ6kqAiS2FfsuPN
|
||||
vg57fhfHbL1yJ4oDbNNNeI0kJTGchXqerr8C20khU/cQ2Xt31VyEZtnTB665Ceugv
|
||||
kp3t2qd8UpAVKl430S5Quqx2ymfUIdxdW08CEjnoRNEL3aOWOXfbf4gSVaXmPCR4i
|
||||
LSIeXnd14lQYK/uxW/8cTFjcmddsKxeXysoQxbSa9VdDK+KkpZdgYXYrTTofXs6v+
|
||||
4afAEhRaaY+MCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
)
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function webfinger_rfc7033($webbie,$zot = false) {
|
||||
|
||||
|
||||
@@ -1473,8 +1460,8 @@ function find_webfinger_location($j,$rhs) {
|
||||
|
||||
function match_webfinger_location($s,$h) {
|
||||
|
||||
// GNU-social and the older StatusNet
|
||||
if(preg_match('|' . $h . '/user/([0-9]*?)$|',$s))
|
||||
// GNU-social and the older StatusNet - the $host/user/123 form doesn't work
|
||||
if(preg_match('|' . $h . '/index.php/user/([0-9]*?)$|',$s))
|
||||
return $s;
|
||||
// Redmatrix / hubzilla
|
||||
if(preg_match('|' . $h . '/channel/|',$s))
|
||||
@@ -1549,7 +1536,7 @@ function fetch_lrdd_template($host) {
|
||||
|
||||
function fetch_xrd_links($url) {
|
||||
|
||||
logger('fetch_xrd_links: ' . $url);
|
||||
logger('fetch_xrd_links: ' . $url, LOGGER_DEBUG);
|
||||
|
||||
$redirects = 0;
|
||||
$x = z_fetch_url($url,false,$redirects,array('timeout' => 20));
|
||||
@@ -1595,6 +1582,10 @@ logger('fetch_xrd_links: ' . $url);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($arr['xrd']['subject'])) {
|
||||
$links[]['@attributes'] = array('rel' => 'subject' , 'href' => $arr['xrd']['subject']);
|
||||
}
|
||||
|
||||
logger('fetch_xrd_links: ' . print_r($links,true), LOGGER_DATA);
|
||||
|
||||
return $links;
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once('include/crypto.php');
|
||||
|
||||
function get_salmon_key($uri,$keyhash) {
|
||||
$ret = array();
|
||||
|
||||
logger('Fetching salmon key for ' . $uri, LOGGER_DEBUG, LOG_INFO);
|
||||
|
||||
$x = webfinger_rfc7033($uri,true);
|
||||
|
||||
logger('webfinger returns: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG);
|
||||
|
||||
if($x && array_key_exists('links',$x) && $x['links']) {
|
||||
foreach($x['links'] as $link) {
|
||||
if(array_key_exists('rel',$link) && $link['rel'] === 'magic-public-key') {
|
||||
$ret[] = $link['href'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
$arr = old_webfinger($uri);
|
||||
|
||||
logger('old webfinger returns: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG);
|
||||
|
||||
if(is_array($arr)) {
|
||||
foreach($arr as $a) {
|
||||
if($a['@attributes']['rel'] === 'magic-public-key') {
|
||||
$ret[] = $a['@attributes']['href'];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// We have found at least one key URL
|
||||
// If it's inline, parse it - otherwise get the key
|
||||
|
||||
if(count($ret)) {
|
||||
for($x = 0; $x < count($ret); $x ++) {
|
||||
if(substr($ret[$x],0,5) === 'data:') {
|
||||
$ret[$x] = convert_salmon_key($ret[$x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
logger('Key located: ' . print_r($ret,true), LOGGER_DEBUG, LOG_INFO);
|
||||
|
||||
if(count($ret) == 1) {
|
||||
|
||||
// We only found one one key so we don't care if the hash matches.
|
||||
// If it's the wrong key we'll find out soon enough because
|
||||
// message verification will fail. This also covers some older
|
||||
// software which don't supply a keyhash. As long as they only
|
||||
// have one key we'll be right.
|
||||
|
||||
return $ret[0];
|
||||
}
|
||||
else {
|
||||
foreach($ret as $a) {
|
||||
$hash = base64url_encode(hash('sha256',$a));
|
||||
if($hash == $keyhash)
|
||||
return $a;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function slapper($owner,$url,$slap) {
|
||||
|
||||
// does contact have a salmon endpoint?
|
||||
|
||||
if(! strlen($url))
|
||||
return;
|
||||
|
||||
|
||||
if(! $owner['channel_prvkey']) {
|
||||
logger(sprintf("channel '%s' (%d) does not have a salmon private key. Send failed.",
|
||||
$owner['channel_address'],$owner['channel_id']));
|
||||
return;
|
||||
}
|
||||
|
||||
logger('slapper called for ' .$url . '. Data: ' . $slap, LOGGER_DATA, LOG_DEBUG);
|
||||
|
||||
// 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['channel_pubkey'])),true);
|
||||
|
||||
// precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods
|
||||
|
||||
$precomputed = '.YXBwbGljYXRpb24vYXRvbSt4bWw=.YmFzZTY0dXJs.UlNBLVNIQTI1Ng==';
|
||||
|
||||
$signature = base64url_encode(rsa_sign(str_replace('=','',$data . $precomputed),$owner['channel_prvkey']));
|
||||
|
||||
$signature2 = base64url_encode(rsa_sign($data . $precomputed,$owner['channel_prvkey']));
|
||||
|
||||
$signature3 = base64url_encode(rsa_sign($data,$owner['channel_prvkey']));
|
||||
|
||||
$salmon_tpl = get_markup_template('magicsig.tpl');
|
||||
|
||||
$salmon = replace_macros($salmon_tpl,array(
|
||||
'$data' => $data,
|
||||
'$encoding' => $encoding,
|
||||
'$algorithm' => $algorithm,
|
||||
'$keyhash' => $keyhash,
|
||||
'$signature' => $signature
|
||||
));
|
||||
|
||||
// slap them
|
||||
|
||||
$redirects = 0;
|
||||
|
||||
$ret = z_post_url($url,$salmon, $redirects, array('headers' => array(
|
||||
'Content-type: application/magic-envelope+xml',
|
||||
'Content-length: ' . strlen($salmon))
|
||||
));
|
||||
|
||||
|
||||
$return_code = $ret['return_code'];
|
||||
|
||||
// check for success, e.g. 2xx
|
||||
|
||||
if($return_code > 299) {
|
||||
|
||||
logger('compliant salmon failed. Falling back to status.net hack2');
|
||||
|
||||
// Entirely likely that their salmon implementation is
|
||||
// non-compliant. Let's try once more, this time only signing
|
||||
// the data, without stripping '=' chars
|
||||
|
||||
$salmon = replace_macros($salmon_tpl,array(
|
||||
'$data' => $data,
|
||||
'$encoding' => $encoding,
|
||||
'$algorithm' => $algorithm,
|
||||
'$keyhash' => $keyhash,
|
||||
'$signature' => $signature2
|
||||
));
|
||||
|
||||
$redirects = 0;
|
||||
|
||||
$ret = z_post_url($url,$salmon, $redirects, array('headers' => array(
|
||||
'Content-type: application/magic-envelope+xml',
|
||||
'Content-length: ' . strlen($salmon))
|
||||
));
|
||||
|
||||
|
||||
$return_code = $ret['return_code'];
|
||||
|
||||
if($return_code > 299) {
|
||||
|
||||
logger('compliant salmon failed. Falling back to status.net hack3');
|
||||
|
||||
// Entirely likely that their salmon implementation is
|
||||
// non-compliant. Let's try once more, this time only signing
|
||||
// the data, without the precomputed blob
|
||||
|
||||
$salmon = replace_macros($salmon_tpl,array(
|
||||
'$data' => $data,
|
||||
'$encoding' => $encoding,
|
||||
'$algorithm' => $algorithm,
|
||||
'$keyhash' => $keyhash,
|
||||
'$signature' => $signature3
|
||||
));
|
||||
|
||||
$redirects = 0;
|
||||
|
||||
$ret = z_post_url($url,$salmon, $redirects, array('headers' => array(
|
||||
'Content-type: application/magic-envelope+xml',
|
||||
'Content-length: ' . strlen($salmon))
|
||||
));
|
||||
|
||||
|
||||
$return_code = $ret['return_code'];
|
||||
}
|
||||
}
|
||||
logger('slapper for ' . $url . ' returned ' . $return_code);
|
||||
|
||||
if(! $return_code)
|
||||
return(-1);
|
||||
if(($return_code == 503) && (stristr($ret['header'],'retry-after')))
|
||||
return(-1);
|
||||
|
||||
return ((($return_code >= 200) && ($return_code < 300)) ? 0 : 1);
|
||||
}
|
||||
|
||||
@@ -1314,7 +1314,7 @@ function widget_admin($arr) {
|
||||
|
||||
$aside = array(
|
||||
'site' => array(z_root() . '/admin/site/', t('Site'), 'site'),
|
||||
'users' => array(z_root() . '/admin/users/', t('Accounts'), 'users'),
|
||||
'users' => array(z_root() . '/admin/users/', t('Accounts'), 'users', 'pending-update', t('Member registrations waiting for confirmation')),
|
||||
'channels' => array(z_root() . '/admin/channels/', t('Channels'), 'channels'),
|
||||
'security' => array(z_root() . '/admin/security/', t('Security'), 'security'),
|
||||
'features' => array(z_root() . '/admin/features/', t('Features'), 'features'),
|
||||
@@ -1330,24 +1330,29 @@ function widget_admin($arr) {
|
||||
|
||||
$r = q("SELECT * FROM addon WHERE plugin_admin = 1");
|
||||
|
||||
$aside['plugins_admin'] = array();
|
||||
$plugins = array();
|
||||
if($r) {
|
||||
foreach ($r as $h){
|
||||
$plugin = $h['name'];
|
||||
$aside['plugins_admin'][] = array(z_root() . '/admin/plugins/' . $plugin, $plugin, 'plugin');
|
||||
$plugins[] = array(z_root() . '/admin/plugins/' . $plugin, $plugin, 'plugin');
|
||||
// temp plugins with admin
|
||||
$a->plugins_admin[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
$aside['logs'] = array(z_root() . '/admin/logs/', t('Logs'), 'logs');
|
||||
$logs = array(z_root() . '/admin/logs/', t('Logs'), 'logs');
|
||||
|
||||
$arr = array('links' => $aside,'plugins' => $plugins,'logs' => $logs);
|
||||
call_hooks('admin_aside',$arr);
|
||||
|
||||
$o .= replace_macros(get_markup_template('admin_aside.tpl'), array(
|
||||
'$admin' => $aside,
|
||||
'$admtxt' => t('Admin'),
|
||||
'$plugadmtxt' => t('Plugin Features'),
|
||||
'$plugins' => $plugins,
|
||||
'$logtxt' => t('Logs'),
|
||||
'$h_pending' => t('User registrations waiting for confirmation'),
|
||||
'$logs' => $logs,
|
||||
'$h_pending' => t('Member registrations waiting for confirmation'),
|
||||
'$admurl'=> z_root() . '/admin/'
|
||||
));
|
||||
|
||||
|
||||
@@ -2278,6 +2278,12 @@ function check_location_move($sender_hash,$locations) {
|
||||
dbesc($sender_hash)
|
||||
);
|
||||
|
||||
// federation plugins may wish to notify connections
|
||||
// of the move on singleton networks
|
||||
|
||||
$arr = array('channel' => $r[0],'locations' => $locations);
|
||||
call_hooks('location_move',$arr);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user