function init() { $uri = urldecode(notags(trim($_GET['uri']))); logger('xrd: ' . $uri, LOGGER_DEBUG); $resource = $uri; if (substr($uri, 0, 4) === 'http') { $uri = str_replace('~', '', $uri); $name = basename($uri); } else { $local = str_replace('acct:', '', $uri); if (substr($local, 0, 2) == '//') { $local = substr($local, 2); } $name = substr($local, 0, strpos($local, '@')); } $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", dbesc($name)); if (!$r) { killme(); } $dspr = replace_macros(get_markup_template('xrd_diaspora.tpl'), array('$baseurl' => z_root(), '$dspr_guid' => $r[0]['channel_guid'] . str_replace('.', '', \App::get_hostname()), '$dspr_key' => base64_encode(pemtorsa($r[0]['channel_pubkey'])))); $salmon_key = salmon_key($r[0]['channel_pubkey']); header('Access-Control-Allow-Origin: *'); header("Content-type: application/xrd+xml"); $aliases = array('acct:' . channel_reddress($r[0]), z_root() . '/channel/' . $r[0]['channel_address'], z_root() . '/~' . $r[0]['channel_address']); for ($x = 0; $x < count($aliases); $x++) { if ($aliases[$x] === $resource) { unset($aliases[$x]); } } $o = replace_macros(get_markup_template('xrd_person.tpl'), array('$nick' => $r[0]['channel_address'], '$accturi' => $resource, '$aliases' => $aliases, '$profile_url' => z_root() . '/channel/' . $r[0]['channel_address'], '$hcard_url' => z_root() . '/hcard/' . $r[0]['channel_address'], '$atom' => z_root() . '/feed/' . $r[0]['channel_address'], '$zot_post' => z_root() . '/post/' . $r[0]['channel_address'], '$poco_url' => z_root() . '/poco/' . $r[0]['channel_address'], '$photo' => z_root() . '/photo/profile/l/' . $r[0]['channel_id'], '$dspr' => $dspr, '$modexp' => 'data:application/magic-public-key,' . $salmon_key, '$subscribe' => z_root() . '/follow?url={uri}', '$bigkey' => salmon_key($r[0]['channel_pubkey']))); $arr = array('user' => $r[0], 'xml' => $o); call_hooks('personal_xrd', $arr); echo $arr['xml']; killme(); }
function xrd_init(&$a) { $uri = urldecode(notags(trim($_GET['uri']))); if (substr($uri, 0, 4) === 'http') { $name = basename($uri); } else { $local = str_replace('acct:', '', $uri); if (substr($local, 0, 2) == '//') { $local = substr($local, 2); } $name = substr($local, 0, strpos($local, '@')); } $r = q("SELECT * FROM `user` WHERE `nickname` = '%s' LIMIT 1", dbesc($name)); if (!count($r)) { killme(); } $salmon_key = salmon_key($r[0]['spubkey']); header('Access-Control-Allow-Origin: *'); header("Content-type: text/xml"); if (get_config('system', 'diaspora_enabled')) { //$tpl = file_get_contents('view/xrd_diaspora.tpl'); $tpl = get_markup_template('xrd_diaspora.tpl'); $dspr = replace_macros($tpl, array('$baseurl' => $a->get_baseurl(), '$dspr_guid' => $r[0]['guid'], '$dspr_key' => base64_encode(pemtorsa($r[0]['pubkey'])))); } else { $dspr = ''; } //$tpl = file_get_contents('view/xrd_person.tpl'); $tpl = get_markup_template('xrd_person.tpl'); $o = replace_macros($tpl, array('$nick' => $r[0]['nickname'], '$accturi' => $uri, '$profile_url' => $a->get_baseurl() . '/profile/' . $r[0]['nickname'], '$hcard_url' => $a->get_baseurl() . '/hcard/' . $r[0]['nickname'], '$atom' => $a->get_baseurl() . '/dfrn_poll/' . $r[0]['nickname'], '$zot_post' => $a->get_baseurl() . '/post/' . $r[0]['nickname'], '$poco_url' => $a->get_baseurl() . '/poco/' . $r[0]['nickname'], '$photo' => $a->get_baseurl() . '/photo/profile/' . $r[0]['uid'] . '.jpg', '$dspr' => $dspr, '$salmon' => $a->get_baseurl() . '/salmon/' . $r[0]['nickname'], '$salmen' => $a->get_baseurl() . '/salmon/' . $r[0]['nickname'] . '/mention', '$subscribe' => $a->get_baseurl() . '/follow?url={uri}', '$modexp' => 'data:application/magic-public-key,' . $salmon_key, '$bigkey' => salmon_key($r[0]['pubkey']))); $arr = array('user' => $r[0], 'xml' => $o); call_hooks('personal_xrd', $arr); echo $arr['xml']; killme(); }
function xrd_init(&$a) { $uri = urldecode(notags(trim($_GET['uri']))); if (substr($uri, 0, 4) === 'http') { $name = basename($uri); } else { $local = str_replace('acct:', '', $uri); if (substr($local, 0, 2) == '//') { $local = substr($local, 2); } $name = substr($local, 0, strpos($local, '@')); } $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", dbesc($name)); if (!$r) { killme(); } $dspr = replace_macros(get_markup_template('xrd_diaspora.tpl'), array('$baseurl' => $a->get_baseurl(), '$dspr_guid' => $r[0]['channel_guid'], '$dspr_key' => base64_encode(pemtorsa($r[0]['channel_pubkey'])))); $salmon_key = salmon_key($r[0]['channel_pubkey']); header('Access-Control-Allow-Origin: *'); header("Content-type: text/xml"); $tpl = get_markup_template('view/xrd_person.tpl'); $o = replace_macros(get_markup_template('xrd_person.tpl'), array('$nick' => $r[0]['channel_address'], '$accturi' => $uri, '$profile_url' => $a->get_baseurl() . '/channel/' . $r[0]['channel_address'], '$hcard_url' => $a->get_baseurl() . '/hcard/' . $r[0]['channel_address'], '$atom' => $a->get_baseurl() . '/feed/' . $r[0]['channel_address'], '$zot_post' => $a->get_baseurl() . '/post/' . $r[0]['channel_address'], '$poco_url' => $a->get_baseurl() . '/poco/' . $r[0]['channel_address'], '$photo' => $a->get_baseurl() . '/photo/profile/l/' . $r[0]['channel_id'], '$dspr' => $dspr, '$modexp' => 'data:application/magic-public-key,' . $salmon_key)); $arr = array('user' => $r[0], 'xml' => $o); call_hooks('personal_xrd', $arr); echo $arr['xml']; killme(); }
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; }
function hostxrd_init(&$a) { header('Access-Control-Allow-Origin: *'); header("Content-type: text/xml"); $pubkey = get_config('system', 'site_pubkey'); if (!$pubkey) { $res = new_keypair(1024); set_config('system', 'site_prvkey', $res['prvkey']); set_config('system', 'site_pubkey', $res['pubkey']); } $tpl = file_get_contents('view/xrd_host.tpl'); echo str_replace(array('$zhost', '$zroot', '$domain', '$zot_post', '$bigkey'), array($a->get_hostname(), z_root(), z_path(), z_root() . '/post', salmon_key(get_config('system', 'site_pubkey'))), $tpl); session_write_close(); exit; }
function hostxrd_init(&$a) { header('Access-Control-Allow-Origin: *'); header("Content-type: text/xml"); $pubkey = get_config('system', 'site_pubkey'); if (!$pubkey) { // should only have to ever do this once. $res = openssl_pkey_new(array('digest_alg' => 'sha1', 'private_key_bits' => 4096, 'encrypt_key' => false)); $prvkey = ''; openssl_pkey_export($res, $prvkey); // Get public key $pkey = openssl_pkey_get_details($res); $pubkey = $pkey["key"]; set_config('system', 'site_prvkey', $prvkey); set_config('system', 'site_pubkey', $pubkey); } $tpl = file_get_contents('view/xrd_host.tpl'); echo str_replace(array('$zhost', '$zroot', '$domain', '$zot_post', '$bigkey'), array($a->get_hostname(), z_root(), z_path(), z_root() . '/post', salmon_key(get_config('system', 'site_pubkey'))), $tpl); session_write_close(); exit; }
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, false); // do not strip padding $data_type = 'application/atom+xml'; $encoding = 'base64url'; $algorithm = 'RSA-SHA256'; $keyhash = base64url_encode(hash('sha256', salmon_key($owner['channel_pubkey'])), true); $data = str_replace(array(" ", "\t", "\r", "\n"), array("", "", "", ""), $data); // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods $precomputed = '.YXBwbGljYXRpb24vYXRvbSt4bWw=.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; $signature = base64url_encode(rsa_sign($data . $precomputed, $owner['channel_prvkey'])); $salmon_tpl = get_markup_template('magicsig.tpl', 'addon/gnusoc/'); $salmon = replace_macros($salmon_tpl, array('$data' => $data, '$encoding' => $encoding, '$algorithm' => $algorithm, '$keyhash' => $keyhash, '$signature' => $signature)); logger('salmon: ' . $salmon, LOGGER_DATA); $hash = random_string(); queue_insert(array('hash' => $hash, 'account_id' => $owner['channel_account_id'], 'channel_id' => $owner['channel_id'], 'driver' => 'slap', 'posturl' => $url, 'notify' => '', 'msg' => $salmon)); return $hash; }
function slapper($owner, $url, $slap) { logger('slapper called for ' . $url . '. Data: ' . $slap); // does contact have a salmon endpoint? if (!strlen($url)) { return; } if (!$owner['sprvkey']) { logger(sprintf("user '%s' (%d) does not have a salmon private key. Send failed.", $owner['username'], $owner['uid'])); 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" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" \t xmlns:statusnet="http://status.net/schema/api/1/" >\t\t\t\t\t\t\t\t\t\t\t\t\t> 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'])), 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['sprvkey'])); $signature2 = base64url_encode(rsa_sign($data . $precomputed, $owner['sprvkey'])); $signature3 = base64url_encode(rsa_sign($data, $owner['sprvkey'])); $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 post_url($url, $salmon, array('Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon))); $a = get_app(); $return_code = $a->get_curl_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)); // slap them post_url($url, $salmon, array('Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon))); $return_code = $a->get_curl_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)); // slap them post_url($url, $salmon, array('Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon))); $return_code = $a->get_curl_code(); } } logger('slapper for ' . $url . ' returned ' . $return_code); if (!$return_code) { return -1; } if ($return_code == 503 && stristr($a->get_curl_headers(), 'retry-after')) { return -1; } return $return_code >= 200 && $return_code < 300 ? 0 : 1; }
function init() { $result = array(); $scheme = ''; if (x($_SERVER, 'HTTPS') && $_SERVER['HTTPS']) { $scheme = 'https'; } elseif (x($_SERVER, 'SERVER_PORT') && intval($_SERVER['SERVER_PORT']) == 443) { $scheme = 'https'; } $zot = intval($_REQUEST['zot']); if ($scheme !== 'https' && !$zot) { header($_SERVER["SERVER_PROTOCOL"] . ' ' . 500 . ' ' . 'Webfinger requires HTTPS'); killme(); } $resource = $_REQUEST['resource']; logger('webfinger: ' . $resource, LOGGER_DEBUG); $r = null; if ($resource) { if (strpos($resource, 'acct:') === 0) { $channel = str_replace('acct:', '', $resource); if (strpos($channel, '@') !== false) { $host = substr($channel, strpos($channel, '@') + 1); if (strcasecmp($host, \App::get_hostname())) { goaway('https://' . $host . '/.well-known/webfinger?f=&resource=' . $resource . ($zot ? '&zot=' . $zot : '')); } $channel = substr($channel, 0, strpos($channel, '@')); } } if (strpos($resource, 'http') === 0) { $channel = str_replace('~', '', basename($resource)); } $r = q("select * from channel left join xchan on channel_hash = xchan_hash \n\t\t\t\twhere channel_address = '%s' limit 1", dbesc($channel)); } header('Access-Control-Allow-Origin: *'); if ($resource && $r) { $h = q("select hubloc_addr from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", dbesc($r[0]['channel_hash'])); $result['subject'] = $resource; $aliases = array(z_root() . '/channel/' . $r[0]['channel_address'], z_root() . '/~' . $r[0]['channel_address']); if ($h) { foreach ($h as $hh) { $aliases[] = 'acct:' . $hh['hubloc_addr']; } } $result['aliases'] = array(); $result['properties'] = array('http://webfinger.net/ns/name' => $r[0]['channel_name'], 'http://xmlns.com/foaf/0.1/name' => $r[0]['channel_name']); foreach ($aliases as $alias) { if ($alias != $resource) { $result['aliases'][] = $alias; } } $result['links'] = array(array('rel' => 'http://webfinger.net/rel/avatar', 'type' => $r[0]['xchan_photo_mimetype'], 'href' => $r[0]['xchan_photo_l']), array('rel' => 'http://webfinger.net/rel/profile-page', 'href' => z_root() . '/profile/' . $r[0]['channel_address']), array('rel' => 'http://webfinger.net/rel/blog', 'href' => z_root() . '/channel/' . $r[0]['channel_address']), array('rel' => 'http://ostatus.org/schema/1.0/subscribe', 'template' => z_root() . '/follow/url={uri}'), array('rel' => 'http://purl.org/zot/protocol', 'href' => z_root() . '/.well-known/zot-info' . '?address=' . $r[0]['xchan_addr']), array('rel' => 'magic-public-key', 'href' => 'data:application/magic-public-key,' . salmon_key($r[0]['channel_pubkey']))); if ($zot) { // get a zotinfo packet and return it with webfinger $result['zot'] = zotinfo(array('address' => $r[0]['xchan_addr'])); } } else { header($_SERVER["SERVER_PROTOCOL"] . ' ' . 400 . ' ' . 'Bad Request'); killme(); } $arr = array('channel' => $r[0], 'request' => $_REQUEST, 'result' => $result); call_hooks('webfinger', $arr); json_return_and_die($arr['result'], 'application/jrd+json'); }