Merge branch '1.0.x' of git://gitorious.org/statusnet/mainline

Conflicts:
	actions/public.php
This commit is contained in:
Ian Denhardt
2011-03-15 12:42:58 -04:00
1779 changed files with 148840 additions and 140433 deletions

View File

@@ -145,12 +145,10 @@ class OStatusPlugin extends Plugin
$user = $feed->getUser();
$id = $user->id;
$profile = $user->getProfile();
$feed->setActivitySubject($profile->asActivityNoun('subject'));
} else if ($feed instanceof AtomGroupNoticeFeed) {
$salmonAction = 'groupsalmon';
$group = $feed->getGroup();
$id = $group->id;
$feed->setActivitySubject($group->asActivitySubject());
} else {
return true;
}
@@ -421,12 +419,12 @@ class OStatusPlugin extends Plugin
}
function onEndShowStatusNetStyles($action) {
$action->cssLink('plugins/OStatus/theme/base/css/ostatus.css');
$action->cssLink($this->path('theme/base/css/ostatus.css'));
return true;
}
function onEndShowStatusNetScripts($action) {
$action->script('plugins/OStatus/js/ostatus.js');
$action->script($this->path('js/ostatus.js'));
return true;
}
@@ -994,17 +992,29 @@ class OStatusPlugin extends Plugin
return false;
}
function onStartGetProfileFromURI($uri, &$profile) {
function onStartGetProfileFromURI($uri, &$profile)
{
// Don't want to do Web-based discovery on our own server,
// so we check locally first.
// XXX: do discovery here instead (OStatus_profile::ensureProfileURI($uri))
$user = User::staticGet('uri', $uri);
if (!empty($user)) {
$profile = $user->getProfile();
return false;
}
$oprofile = Ostatus_profile::staticGet('uri', $uri);
// Now, check remotely
if (!empty($oprofile) && !$oprofile->isGroup()) {
$oprofile = Ostatus_profile::ensureProfileURI($uri);
if (!empty($oprofile)) {
$profile = $oprofile->localProfile();
return false;
}
// Still not a hit, so give up.
return true;
}

View File

@@ -47,6 +47,9 @@ class GroupsalmonAction extends SalmonAction
$this->clientError(_m('No such group.'));
}
$this->target = $this->group;
$oprofile = Ostatus_profile::staticGet('group_id', $id);
if ($oprofile) {
// TRANS: Client error.

View File

@@ -115,10 +115,15 @@ class OStatusInitAction extends Action
$this->elementStart('ul', 'form_data');
$this->elementStart('li', array('id' => 'ostatus_nickname'));
// TRANS: Field label.
$this->input('nickname', _m('User nickname'), $this->nickname,
_m('Nickname of the user you want to follow.'));
$this->hidden('group', $this->group); // pass-through for magic links
if ($this->group) {
// TRANS: Field label.
$this->input('group', _m('Group nickname'), $this->group,
_m('Nickname of the group you want to join.'));
} else {
// TRANS: Field label.
$this->input('nickname', _m('User nickname'), $this->nickname,
_m('Nickname of the user you want to follow.'));
}
$this->elementEnd('li');
$this->elementStart('li', array('id' => 'ostatus_profile'));
// TRANS: Field label.

View File

@@ -163,65 +163,41 @@ class OStatusSubAction extends Action
}
$this->elementStart('div', 'entity_profile vcard');
$this->elementStart('dl', 'entity_depiction');
$this->element('dt', null, _m('Photo'));
$this->elementStart('dd');
$this->element('img', array('src' => $avatar,
'class' => 'photo avatar',
'class' => 'photo avatar entity_depiction',
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $nickname));
$this->elementEnd('dd');
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_nickname');
$this->element('dt', null, _m('Nickname'));
$this->elementStart('dd');
$hasFN = ($fullname !== '') ? 'nickname' : 'fn nickname';
$hasFN = ($fullname !== '') ? 'nickname' : 'fn nickname entity_nickname';
$this->elementStart('a', array('href' => $profile,
'class' => 'url '.$hasFN));
$this->raw($nickname);
$this->elementEnd('a');
$this->elementEnd('dd');
$this->elementEnd('dl');
if (!is_null($fullname)) {
$this->elementStart('dl', 'entity_fn');
$this->elementStart('dd');
$this->elementStart('span', 'fn');
$this->elementStart('div', 'fn entity_fn');
$this->raw($fullname);
$this->elementEnd('span');
$this->elementEnd('dd');
$this->elementEnd('dl');
$this->elementEnd('div');
}
if (!is_null($location)) {
$this->elementStart('dl', 'entity_location');
$this->element('dt', null, _m('Location'));
$this->elementStart('dd', 'label');
$this->elementStart('div', 'label entity_location');
$this->raw($location);
$this->elementEnd('dd');
$this->elementEnd('dl');
$this->elementEnd('div');
}
if (!is_null($homepage)) {
$this->elementStart('dl', 'entity_url');
$this->element('dt', null, _m('URL'));
$this->elementStart('dd');
$this->elementStart('a', array('href' => $homepage,
'class' => 'url'));
'class' => 'url entity_url'));
$this->raw($homepage);
$this->elementEnd('a');
$this->elementEnd('dd');
$this->elementEnd('dl');
}
if (!is_null($note)) {
$this->elementStart('dl', 'entity_note');
$this->element('dt', null, _m('Note'));
$this->elementStart('dd', 'note');
$this->elementStart('div', 'note entity_note');
$this->raw($note);
$this->elementEnd('dd');
$this->elementEnd('dl');
$this->elementEnd('div');
}
$this->elementEnd('div');
}

View File

@@ -43,6 +43,8 @@ class UsersalmonAction extends SalmonAction
$this->clientError(_m('No such user.'));
}
$this->target = $this->user;
return true;
}

View File

@@ -487,7 +487,15 @@ class FeedSub extends Memcached_DataObject
if ($their_hmac === $our_hmac) {
return true;
}
common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bad SHA-1 HMAC: got $their_hmac, expected $our_hmac");
if (common_config('feedsub', 'debug')) {
$tempfile = tempnam(sys_get_temp_dir(), 'feedsub-receive');
if ($tempfile) {
file_put_contents($tempfile, $post);
}
common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bad SHA-1 HMAC: got $their_hmac, expected $our_hmac for feed $this->uri on $this->huburi; saved to $tempfile");
} else {
common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bad SHA-1 HMAC: got $their_hmac, expected $our_hmac for feed $this->uri on $this->huburi");
}
} else {
common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bogus HMAC '$hmac'");
}

View File

@@ -39,11 +39,41 @@ class Magicsig extends Memcached_DataObject
public $__table = 'magicsig';
/**
* Key to user.id/profile.id for the local user whose key we're storing.
*
* @var int
*/
public $user_id;
/**
* Flattened string representation of the key pair; callers should
* usually use $this->publicKey and $this->privateKey directly,
* which hold live Crypt_RSA key objects.
*
* @var string
*/
public $keypair;
/**
* Crypto algorithm used for this key; currently only RSA-SHA256 is supported.
*
* @var string
*/
public $alg;
/**
* Public RSA key; gets serialized in/out via $this->keypair string.
*
* @var Crypt_RSA
*/
public $publicKey;
/**
* PrivateRSA key; gets serialized in/out via $this->keypair string.
*
* @var Crypt_RSA
*/
public $privateKey;
public function __construct($alg = 'RSA-SHA256')
@@ -51,6 +81,13 @@ class Magicsig extends Memcached_DataObject
$this->alg = $alg;
}
/**
* Fetch a Magicsig object from the cache or database on a field match.
*
* @param string $k
* @param mixed $v
* @return Magicsig
*/
public /*static*/ function staticGet($k, $v=null)
{
$obj = parent::staticGet(__CLASS__, $k, $v);
@@ -103,6 +140,14 @@ class Magicsig extends Memcached_DataObject
return array(false, false, false);
}
/**
* Save this keypair into the database.
*
* Overloads default insert behavior to encode the live key objects
* as a flat string for storage.
*
* @return mixed
*/
function insert()
{
$this->keypair = $this->toString();
@@ -110,6 +155,14 @@ class Magicsig extends Memcached_DataObject
return parent::insert();
}
/**
* Generate a new keypair for a local user and store in the database.
*
* Warning: this can be very slow on systems without the GMP module.
* Runtimes of 20-30 seconds are not unheard-of.
*
* @param int $user_id id of local user we're creating a key for
*/
public function generate($user_id)
{
$rsa = new Crypt_RSA();
@@ -128,6 +181,12 @@ class Magicsig extends Memcached_DataObject
$this->insert();
}
/**
* Encode the keypair or public key as a string.
*
* @param boolean $full_pair set to false to leave out the private key.
* @return string
*/
public function toString($full_pair = true)
{
$mod = Magicsig::base64_url_encode($this->publicKey->modulus->toBytes());
@@ -140,6 +199,13 @@ class Magicsig extends Memcached_DataObject
return 'RSA.' . $mod . '.' . $exp . $private_exp;
}
/**
* Decode a string representation of an RSA public key or keypair
* as a Magicsig object which can be used to sign or verify.
*
* @param string $text
* @return Magicsig
*/
public static function fromString($text)
{
$magic_sig = new Magicsig();
@@ -168,6 +234,14 @@ class Magicsig extends Memcached_DataObject
return $magic_sig;
}
/**
* Fill out $this->privateKey or $this->publicKey with a Crypt_RSA object
* representing the give key (as mod/exponent pair).
*
* @param string $mod base64-encoded
* @param string $exp base64-encoded exponent
* @param string $type one of 'public' or 'private'
*/
public function loadKey($mod, $exp, $type = 'public')
{
common_log(LOG_DEBUG, "Adding ".$type." key: (".$mod .', '. $exp .")");
@@ -186,11 +260,22 @@ class Magicsig extends Memcached_DataObject
}
}
/**
* Returns the name of the crypto algorithm used for this key.
*
* @return string
*/
public function getName()
{
return $this->alg;
}
/**
* Returns the name of a hash function to use for signing with this key.
*
* @return string
* @fixme is this used? doesn't seem to be called by name.
*/
public function getHash()
{
switch ($this->alg) {
@@ -200,24 +285,48 @@ class Magicsig extends Memcached_DataObject
}
}
/**
* Generate base64-encoded signature for the given byte string
* using our private key.
*
* @param string $bytes as raw byte string
* @return string base64-encoded signature
*/
public function sign($bytes)
{
$sig = $this->privateKey->sign($bytes);
return Magicsig::base64_url_encode($sig);
}
/**
*
* @param string $signed_bytes as raw byte string
* @param string $signature as base64
* @return boolean
*/
public function verify($signed_bytes, $signature)
{
$signature = Magicsig::base64_url_decode($signature);
return $this->publicKey->verify($signed_bytes, $signature);
}
/**
* URL-encoding-friendly base64 variant encoding.
*
* @param string $input
* @return string
*/
public static function base64_url_encode($input)
{
return strtr(base64_encode($input), '+/', '-_');
}
/**
* URL-encoding-friendly base64 variant decoding.
*
* @param string $input
* @return string
*/
public static function base64_url_decode($input)
{
return base64_decode(strtr($input, '-_', '+/'));

View File

@@ -293,6 +293,7 @@ class Ostatus_profile extends Managed_DataObject
* an acceptable response from the remote site.
*
* @param mixed $entry XML string, Notice, or Activity
* @param Profile $actor
* @return boolean success
*/
public function notifyActivity($entry, $actor)
@@ -419,7 +420,8 @@ class Ostatus_profile extends Managed_DataObject
{
$activity = new Activity($entry, $feed);
if (Event::handle('StartHandleFeedEntry', array($activity))) {
if (Event::handle('StartHandleFeedEntryWithProfile', array($activity, $this)) &&
Event::handle('StartHandleFeedEntry', array($activity))) {
// @todo process all activity objects
switch ($activity->objects[0]->type) {
@@ -441,6 +443,7 @@ class Ostatus_profile extends Managed_DataObject
}
Event::handle('EndHandleFeedEntry', array($activity));
Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this));
}
}
@@ -453,36 +456,10 @@ class Ostatus_profile extends Managed_DataObject
*/
public function processPost($activity, $method)
{
if ($this->isGroup()) {
// A group feed will contain posts from multiple authors.
// @fixme validate these profiles in some way!
$oprofile = self::ensureActorProfile($activity);
if ($oprofile->isGroup()) {
// Groups can't post notices in StatusNet.
common_log(LOG_WARNING, "OStatus: skipping post with group listed as author: $oprofile->uri in feed from $this->uri");
return false;
}
} else {
$actor = $activity->actor;
$oprofile = $this->checkAuthorship($activity);
if (empty($actor)) {
// OK here! assume the default
} else if ($actor->id == $this->uri || $actor->link == $this->uri) {
$this->updateFromActivityObject($actor);
} else if ($actor->id) {
// We have an ActivityStreams actor with an explicit ID that doesn't match the feed owner.
// This isn't what we expect from mainline OStatus person feeds!
// Group feeds go down another path, with different validation...
// Most likely this is a plain ol' blog feed of some kind which
// doesn't match our expectations. We'll take the entry, but ignore
// the <author> info.
common_log(LOG_WARNING, "Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}");
} else {
// Plain <author> without ActivityStreams actor info.
// We'll just ignore this info for now and save the update under the feed's identity.
}
$oprofile = $this;
if (empty($oprofile)) {
return false;
}
// It's not always an ActivityObject::NOTE, but... let's just say it is.
@@ -897,54 +874,19 @@ class Ostatus_profile extends Managed_DataObject
* @return Ostatus_profile
* @throws Exception
*/
public static function ensureAtomFeed($feedEl, $hints)
{
// Try to get a profile from the feed activity:subject
$author = ActivityUtils::getFeedAuthor($feedEl);
$subject = ActivityUtils::child($feedEl, Activity::SUBJECT, Activity::SPEC);
if (!empty($subject)) {
$subjObject = new ActivityObject($subject);
return self::ensureActivityObjectProfile($subjObject, $hints);
if (empty($author)) {
// XXX: make some educated guesses here
// TRANS: Feed sub exception.
throw new FeedSubException(_m('Can\'t find enough profile '.
'information to make a feed.'));
}
// Otherwise, try the feed author
$author = ActivityUtils::child($feedEl, Activity::AUTHOR, Activity::ATOM);
if (!empty($author)) {
$authorObject = new ActivityObject($author);
return self::ensureActivityObjectProfile($authorObject, $hints);
}
// Sheesh. Not a very nice feed! Let's try fingerpoken in the
// entries.
$entries = $feedEl->getElementsByTagNameNS(Activity::ATOM, 'entry');
if (!empty($entries) && $entries->length > 0) {
$entry = $entries->item(0);
$actor = ActivityUtils::child($entry, Activity::ACTOR, Activity::SPEC);
if (!empty($actor)) {
$actorObject = new ActivityObject($actor);
return self::ensureActivityObjectProfile($actorObject, $hints);
}
$author = ActivityUtils::child($entry, Activity::AUTHOR, Activity::ATOM);
if (!empty($author)) {
$authorObject = new ActivityObject($author);
return self::ensureActivityObjectProfile($authorObject, $hints);
}
}
// XXX: make some educated guesses here
// TRANS: Feed sub exception.
throw new FeedSubException(_m('Can\'t find enough profile information to make a feed.'));
return self::ensureActivityObjectProfile($author, $hints);
}
/**
@@ -1132,7 +1074,8 @@ class Ostatus_profile extends Managed_DataObject
return $url;
}
}
return common_path('plugins/OStatus/images/96px-Feed-icon.svg.png');
return Plugin::staticPath('OStatus', 'images/96px-Feed-icon.svg.png');
}
/**
@@ -1373,7 +1316,17 @@ class Ostatus_profile extends Managed_DataObject
{
$orig = clone($profile);
$profile->nickname = self::getActivityObjectNickname($object, $hints);
// Existing nickname is better than nothing.
if (!array_key_exists('nickname', $hints)) {
$hints['nickname'] = $profile->nickname;
}
$nickname = self::getActivityObjectNickname($object, $hints);
if (!empty($nickname)) {
$profile->nickname = $nickname;
}
if (!empty($object->title)) {
$profile->fullname = $object->title;
@@ -1389,9 +1342,23 @@ class Ostatus_profile extends Managed_DataObject
$profile->profileurl = $object->id;
}
$profile->bio = self::getActivityObjectBio($object, $hints);
$profile->location = self::getActivityObjectLocation($object, $hints);
$profile->homepage = self::getActivityObjectHomepage($object, $hints);
$bio = self::getActivityObjectBio($object, $hints);
if (!empty($bio)) {
$profile->bio = $bio;
}
$location = self::getActivityObjectLocation($object, $hints);
if (!empty($location)) {
$profile->location = $location;
}
$homepage = self::getActivityObjectHomepage($object, $hints);
if (!empty($homepage)) {
$profile->homepage = $homepage;
}
if (!empty($object->geopoint)) {
$location = ActivityContext::locationFromPoint($object->geopoint);
@@ -1799,12 +1766,55 @@ class Ostatus_profile extends Managed_DataObject
case 'mailto':
$rest = $match[2];
$oprofile = Ostatus_profile::ensureWebfinger($rest);
break;
default:
common_log("Unrecognized URI protocol for profile: $protocol ($uri)");
throw new ServerException("Unrecognized URI protocol for profile: $protocol ($uri)");
break;
}
} else {
throw new ServerException("No URI protocol for profile: ($uri)");
}
}
return $oprofile;
}
function checkAuthorship($activity)
{
if ($this->isGroup()) {
// A group feed will contain posts from multiple authors.
// @fixme validate these profiles in some way!
$oprofile = self::ensureActorProfile($activity);
if ($oprofile->isGroup()) {
// Groups can't post notices in StatusNet.
common_log(LOG_WARNING,
"OStatus: skipping post with group listed as author: ".
"$oprofile->uri in feed from $this->uri");
return false;
}
} else {
$actor = $activity->actor;
if (empty($actor)) {
// OK here! assume the default
} else if ($actor->id == $this->uri || $actor->link == $this->uri) {
$this->updateFromActivityObject($actor);
} else if ($actor->id) {
// We have an ActivityStreams actor with an explicit ID that doesn't match the feed owner.
// This isn't what we expect from mainline OStatus person feeds!
// Group feeds go down another path, with different validation...
// Most likely this is a plain ol' blog feed of some kind which
// doesn't match our expectations. We'll take the entry, but ignore
// the <author> info.
common_log(LOG_WARNING, "Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}");
} else {
// Plain <author> without ActivityStreams actor info.
// We'll just ignore this info for now and save the update under the feed's identity.
}
$oprofile = $this;
}
return $oprofile;
}
}

View File

@@ -1,274 +0,0 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* A sample module to show best practices for StatusNet plugins
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package StatusNet
* @author James Walker <james@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
/**
* This class implements LRDD-based service discovery based on the "Hammer Draft"
* (including webfinger)
*
* @see http://groups.google.com/group/webfinger/browse_thread/thread/9f3d93a479e91bbf
*/
class Discovery
{
const LRDD_REL = 'lrdd';
const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
const UPDATESFROM = 'http://schemas.google.com/g/2010#updates-from';
const HCARD = 'http://microformats.org/profile/hcard';
public $methods = array();
public function __construct()
{
$this->registerMethod('Discovery_LRDD_Host_Meta');
$this->registerMethod('Discovery_LRDD_Link_Header');
$this->registerMethod('Discovery_LRDD_Link_HTML');
}
public function registerMethod($class)
{
$this->methods[] = $class;
}
/**
* Given a "user id" make sure it's normalized to either a webfinger
* acct: uri or a profile HTTP URL.
*/
public static function normalize($user_id)
{
if (substr($user_id, 0, 5) == 'http:' ||
substr($user_id, 0, 6) == 'https:' ||
substr($user_id, 0, 5) == 'acct:') {
return $user_id;
}
if (strpos($user_id, '@') !== FALSE) {
return 'acct:' . $user_id;
}
return 'http://' . $user_id;
}
public static function isWebfinger($user_id)
{
$uri = Discovery::normalize($user_id);
return (substr($uri, 0, 5) == 'acct:');
}
/**
* This implements the actual lookup procedure
*/
public function lookup($id)
{
// Normalize the incoming $id to make sure we have a uri
$uri = $this->normalize($id);
foreach ($this->methods as $class) {
$links = call_user_func(array($class, 'discover'), $uri);
if ($link = Discovery::getService($links, Discovery::LRDD_REL)) {
// Load the LRDD XRD
if (!empty($link['template'])) {
$xrd_uri = Discovery::applyTemplate($link['template'], $uri);
} else {
$xrd_uri = $link['href'];
}
$xrd = $this->fetchXrd($xrd_uri);
if ($xrd) {
return $xrd;
}
}
}
// TRANS: Exception.
throw new Exception(sprintf(_m('Unable to find services for %s.'),$id));
}
public static function getService($links, $service) {
if (!is_array($links)) {
return false;
}
foreach ($links as $link) {
if ($link['rel'] == $service) {
return $link;
}
}
}
public static function applyTemplate($template, $id)
{
$template = str_replace('{uri}', urlencode($id), $template);
return $template;
}
public static function fetchXrd($url)
{
try {
$client = new HTTPClient();
$response = $client->get($url);
} catch (HTTP_Request2_Exception $e) {
return false;
}
if ($response->getStatus() != 200) {
return false;
}
return XRD::parse($response->getBody());
}
}
interface Discovery_LRDD
{
public function discover($uri);
}
class Discovery_LRDD_Host_Meta implements Discovery_LRDD
{
public function discover($uri)
{
if (Discovery::isWebfinger($uri)) {
// We have a webfinger acct: - start with host-meta
list($name, $domain) = explode('@', $uri);
} else {
$domain = parse_url($uri, PHP_URL_HOST);
}
$url = 'http://'. $domain .'/.well-known/host-meta';
$xrd = Discovery::fetchXrd($url);
if ($xrd) {
if ($xrd->host != $domain) {
return false;
}
return $xrd->links;
}
}
}
class Discovery_LRDD_Link_Header implements Discovery_LRDD
{
public function discover($uri)
{
try {
$client = new HTTPClient();
$response = $client->get($uri);
} catch (HTTP_Request2_Exception $e) {
return false;
}
if ($response->getStatus() != 200) {
return false;
}
$link_header = $response->getHeader('Link');
if (!$link_header) {
// return false;
}
return array(Discovery_LRDD_Link_Header::parseHeader($link_header));
}
protected static function parseHeader($header)
{
$lh = new LinkHeader($header);
return array('href' => $lh->href,
'rel' => $lh->rel,
'type' => $lh->type);
}
}
class Discovery_LRDD_Link_HTML implements Discovery_LRDD
{
public function discover($uri)
{
try {
$client = new HTTPClient();
$response = $client->get($uri);
} catch (HTTP_Request2_Exception $e) {
return false;
}
if ($response->getStatus() != 200) {
return false;
}
return Discovery_LRDD_Link_HTML::parse($response->getBody());
}
public function parse($html)
{
$links = array();
preg_match('/<head(\s[^>]*)?>(.*?)<\/head>/is', $html, $head_matches);
$head_html = $head_matches[2];
preg_match_all('/<link\s[^>]*>/i', $head_html, $link_matches);
foreach ($link_matches[0] as $link_html) {
$link_url = null;
$link_rel = null;
$link_type = null;
preg_match('/\srel=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $rel_matches);
if ( isset($rel_matches[3]) ) {
$link_rel = $rel_matches[3];
} else if ( isset($rel_matches[1]) ) {
$link_rel = $rel_matches[1];
}
preg_match('/\shref=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $href_matches);
if ( isset($href_matches[3]) ) {
$link_uri = $href_matches[3];
} else if ( isset($href_matches[1]) ) {
$link_uri = $href_matches[1];
}
preg_match('/\stype=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $type_matches);
if ( isset($type_matches[3]) ) {
$link_type = $type_matches[3];
} else if ( isset($type_matches[1]) ) {
$link_type = $type_matches[1];
}
$links[] = array(
'href' => $link_url,
'rel' => $link_rel,
'type' => $link_type,
);
}
return $links;
}
}

View File

@@ -1,66 +0,0 @@
<?php
/**
* @todo Add file header and documentation.
*/
class LinkHeader
{
var $href;
var $rel;
var $type;
function __construct($str)
{
preg_match('/^<[^>]+>/', $str, $uri_reference);
//if (empty($uri_reference)) return;
$this->href = trim($uri_reference[0], '<>');
$this->rel = array();
$this->type = null;
// remove uri-reference from header
$str = substr($str, strlen($uri_reference[0]));
// parse link-params
$params = explode(';', $str);
foreach ($params as $param) {
if (empty($param)) continue;
list($param_name, $param_value) = explode('=', $param, 2);
$param_name = trim($param_name);
$param_value = preg_replace('(^"|"$)', '', trim($param_value));
// for now we only care about 'rel' and 'type' link params
// TODO do something with the other links-params
switch ($param_name) {
case 'rel':
$this->rel = trim($param_value);
break;
case 'type':
$this->type = trim($param_value);
}
}
}
static function getLink($response, $rel=null, $type=null)
{
$headers = $response->getHeader('Link');
if ($headers) {
// Can get an array or string, so try to simplify the path
if (!is_array($headers)) {
$headers = array($headers);
}
foreach ($headers as $header) {
$lh = new LinkHeader($header);
if ((is_null($rel) || $lh->rel == $rel) &&
(is_null($type) || $lh->type == $type)) {
return $lh->href;
}
}
}
return null;
}
}

View File

@@ -80,21 +80,53 @@ class MagicEnvelope
throw new Exception(_m('Unable to locate signer public key.'));
}
/**
* The current MagicEnvelope spec as used in StatusNet 0.9.7 and later
* includes both the original data and some signing metadata fields as
* the input plaintext for the signature hash.
*
* @param array $env
* @return string
*/
public function signingText($env) {
return implode('.', array($env['data'], // this field is pre-base64'd
Magicsig::base64_url_encode($env['data_type']),
Magicsig::base64_url_encode($env['encoding']),
Magicsig::base64_url_encode($env['alg'])));
}
/**
*
* @param <type> $text
* @param <type> $mimetype
* @param <type> $keypair
* @return array: associative array of envelope properties
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
*/
public function signMessage($text, $mimetype, $keypair)
{
$signature_alg = Magicsig::fromString($keypair);
$armored_text = Magicsig::base64_url_encode($text);
return array(
$env = array(
'data' => $armored_text,
'encoding' => MagicEnvelope::ENCODING,
'data_type' => $mimetype,
'sig' => $signature_alg->sign($armored_text),
'sig' => '',
'alg' => $signature_alg->getName()
);
$env['sig'] = $signature_alg->sign($this->signingText($env));
return $env;
}
/**
* Create an <me:env> XML representation of the envelope.
*
* @param array $env associative array with envelope data
* @return string representation of XML document
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
*/
public function toXML($env) {
$xs = new XMLStringer();
$xs->startXML();
@@ -110,6 +142,16 @@ class MagicEnvelope
return $string;
}
/**
* Extract the contained XML payload, and insert a copy of the envelope
* signature data as an <me:provenance> section.
*
* @param array $env associative array with envelope data
* @return string representation of modified XML document
*
* @fixme in case of XML parsing errors, this will spew to the error log or output
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
*/
public function unfold($env)
{
$dom = new DOMDocument();
@@ -136,6 +178,14 @@ class MagicEnvelope
return $dom->saveXML();
}
/**
* Find the author URI referenced in the given Atom entry.
*
* @param string $text string containing Atom entry XML
* @return mixed URI string or false if XML parsing fails, or null if no author URI can be found
*
* @fixme XML parsing failures will spew to error logs/output
*/
public function getAuthor($text) {
$doc = new DOMDocument();
if (!$doc->loadXML($text)) {
@@ -153,11 +203,30 @@ class MagicEnvelope
}
}
/**
* Check if the author in the Atom entry fragment claims to match
* the given identifier URI.
*
* @param string $text string containing Atom entry XML
* @param string $signer_uri
* @return boolean
*/
public function checkAuthor($text, $signer_uri)
{
return ($this->getAuthor($text) == $signer_uri);
}
/**
* Attempt to verify cryptographic signing for parsed envelope data.
* Requires network access to retrieve public key referenced by the envelope signer.
*
* Details of failure conditions are dumped to output log and not exposed to caller.
*
* @param array $env array representation of magic envelope data, as returned from MagicEnvelope::parse()
* @return boolean
*
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
*/
public function verify($env)
{
if ($env['alg'] != 'RSA-SHA256') {
@@ -187,15 +256,35 @@ class MagicEnvelope
return false;
}
return $verifier->verify($env['data'], $env['sig']);
return $verifier->verify($this->signingText($env), $env['sig']);
}
/**
* Extract envelope data from an XML document containing an <me:env> or <me:provenance> element.
*
* @param string XML source
* @return mixed associative array of envelope data, or false on unrecognized input
*
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
* @fixme will spew errors to logs or output in case of XML parse errors
* @fixme may give fatal errors if some elements are missing or invalid XML
* @fixme calling DOMDocument::loadXML statically triggers warnings in strict mode
*/
public function parse($text)
{
$dom = DOMDocument::loadXML($text);
return $this->fromDom($dom);
}
/**
* Extract envelope data from an XML document containing an <me:env> or <me:provenance> element.
*
* @param DOMDocument $dom
* @return mixed associative array of envelope data, or false on unrecognized input
*
* @fixme it might be easier to work with storing envelope data these in the object instead of passing arrays around
* @fixme may give fatal errors if some elements are missing
*/
public function fromDom($dom)
{
$env_element = $dom->getElementsByTagNameNS(MagicEnvelope::NS, 'env')->item(0);
@@ -218,3 +307,24 @@ class MagicEnvelope
);
}
}
/**
* Variant of MagicEnvelope using the earlier signature form listed in the MagicEnvelope
* spec in early 2010; this was used in StatusNet up through 0.9.6, so for backwards compatiblity
* we still need to accept and sometimes send this format.
*/
class MagicEnvelopeCompat extends MagicEnvelope {
/**
* StatusNet through 0.9.6 used an earlier version of the MagicEnvelope spec
* which used only the input data, without the additional fields, as the plaintext
* for signing.
*
* @param array $env
* @return string
*/
public function signingText($env) {
return $env['data'];
}
}

View File

@@ -38,10 +38,12 @@ class Salmon
/**
* Sign and post the given Atom entry as a Salmon message.
*
* @fixme pass through the actor for signing?
* Side effects: may generate a keypair on-demand for the given user,
* which can be very slow on some systems.
*
* @param string $endpoint_uri
* @param string $xml
* @param string $xml string representation of payload
* @param Profile $actor local user profile whose keys to sign with
* @return boolean success
*/
public function post($endpoint_uri, $xml, $actor)
@@ -50,34 +52,65 @@ class Salmon
return false;
}
try {
$xml = $this->createMagicEnv($xml, $actor);
} catch (Exception $e) {
common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
return false;
}
foreach ($this->formatClasses() as $class) {
try {
$envelope = $this->createMagicEnv($xml, $actor, $class);
} catch (Exception $e) {
common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
return false;
}
$headers = array('Content-Type: application/magic-envelope+xml');
try {
$client = new HTTPClient();
$client->setBody($envelope);
$response = $client->post($endpoint_uri, $headers);
} catch (HTTP_Request2_Exception $e) {
common_log(LOG_ERR, "Salmon ($class) post to $endpoint_uri failed: " . $e->getMessage());
continue;
}
if ($response->getStatus() != 200) {
common_log(LOG_ERR, "Salmon ($class) at $endpoint_uri returned status " .
$response->getStatus() . ': ' . $response->getBody());
continue;
}
$headers = array('Content-Type: application/magic-envelope+xml');
try {
$client = new HTTPClient();
$client->setBody($xml);
$response = $client->post($endpoint_uri, $headers);
} catch (HTTP_Request2_Exception $e) {
common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage());
return false;
// Success!
return true;
}
if ($response->getStatus() != 200) {
common_log(LOG_ERR, "Salmon at $endpoint_uri returned status " .
$response->getStatus() . ': ' . $response->getBody());
return false;
}
return true;
return false;
}
public function createMagicEnv($text, $actor)
/**
* List the magic envelope signature class variants in the order we try them.
* Multiples are needed for backwards-compat with StatusNet prior to 0.9.7,
* which used a draft version of the magic envelope spec.
*/
protected function formatClasses() {
return array('MagicEnvelope', 'MagicEnvelopeCompat');
}
/**
* Encode the given string as a signed MagicEnvelope XML document,
* using the keypair for the given local user profile.
*
* Side effects: will create and store a keypair on-demand if one
* hasn't already been generated for this user. This can be very slow
* on some systems.
*
* @param string $text XML fragment to sign, assumed to be Atom
* @param Profile $actor Profile of a local user to use as signer
* @param string $class to override the magic envelope signature version, pass a MagicEnvelope subclass here
*
* @return string XML string representation of magic envelope
*
* @throws Exception on bad profile input or key generation problems
* @fixme if signing fails, this seems to return the original text without warning. Is there a reason for this?
*/
public function createMagicEnv($text, $actor, $class='MagicEnvelope')
{
$magic_env = new MagicEnvelope();
$magic_env = new $class();
$user = User::staticGet('id', $actor->id);
if ($user->id) {
@@ -101,12 +134,32 @@ class Salmon
return $magic_env->toXML($env);
}
/**
* Check if the given magic envelope is well-formed and correctly signed.
* Needs to have network access to fetch public keys over the web.
* Both current and back-compat signature formats will be checked.
*
* Side effects: exceptions and caching updates may occur during network
* fetches.
*
* @param string $text XML fragment of magic envelope
* @return boolean
*
* @throws Exception on bad profile input or key generation problems
* @fixme could hit fatal errors or spew output on invalid XML
*/
public function verifyMagicEnv($text)
{
$magic_env = new MagicEnvelope();
foreach ($this->formatClasses() as $class) {
$magic_env = new $class();
$env = $magic_env->parse($text);
$env = $magic_env->parse($text);
return $magic_env->verify($env);
if ($magic_env->verify($env)) {
return true;
}
}
return false;
}
}

View File

@@ -30,6 +30,7 @@ class SalmonAction extends Action
{
var $xml = null;
var $activity = null;
var $target = null;
function prepare($args)
{
@@ -82,7 +83,8 @@ class SalmonAction extends Action
StatusNet::setApi(true); // Send smaller error pages
common_log(LOG_DEBUG, "Got a " . $this->activity->verb);
if (Event::handle('StartHandleSalmon', array($this->activity))) {
if (Event::handle('StartHandleSalmonTarget', array($this->activity, $this->target)) &&
Event::handle('StartHandleSalmon', array($this->activity))) {
switch ($this->activity->verb)
{
case ActivityVerb::POST:
@@ -118,6 +120,7 @@ class SalmonAction extends Action
throw new ClientException(_m("Unrecognized activity type."));
}
Event::handle('EndHandleSalmon', array($this->activity));
Event::handle('EndHandleSalmonTarget', array($this->activity, $this->target));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - OStatus\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-12-16 15:08+0000\n"
"PO-Revision-Date: 2010-12-16 15:12:44+0000\n"
"POT-Creation-Date: 2011-01-14 10:29+0000\n"
"PO-Revision-Date: 2011-01-14 10:33:50+0000\n"
"Language-Team: Breton <http://translatewiki.net/wiki/Portal:br>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2010-11-30 20:43:51+0000\n"
"X-Generator: MediaWiki 1.18alpha (r78478); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2011-01-10 18:26:06+0000\n"
"X-Generator: MediaWiki 1.18alpha (r80246); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: br\n"
"X-Message-Group: #out-statusnet-plugin-ostatus\n"
@@ -23,94 +23,94 @@ msgstr ""
#. TRANS: Link description for link to subscribe to a remote user.
#. TRANS: Link text for a user to subscribe to an OStatus user.
#: OStatusPlugin.php:225 OStatusPlugin.php:935
#: OStatusPlugin.php:223 OStatusPlugin.php:933
msgid "Subscribe"
msgstr "Koumanantiñ"
#. TRANS: Link description for link to join a remote group.
#: OStatusPlugin.php:244 OStatusPlugin.php:653 actions/ostatussub.php:109
#: OStatusPlugin.php:242 OStatusPlugin.php:651 actions/ostatussub.php:109
msgid "Join"
msgstr "Stagañ"
#. TRANSLATE: %s is a domain.
#: OStatusPlugin.php:457
#: OStatusPlugin.php:455
#, php-format
msgid "Sent from %s via OStatus"
msgstr "Kaset adalek %s dre OStatus"
#. TRANS: Exception.
#: OStatusPlugin.php:529
#: OStatusPlugin.php:527
msgid "Could not set up remote subscription."
msgstr ""
#: OStatusPlugin.php:603
#: OStatusPlugin.php:601
msgid "Unfollow"
msgstr "Chom hep heuliañ"
#. TRANS: Success message for unsubscribe from user attempt through OStatus.
#. TRANS: %1$s is the unsubscriber's name, %2$s is the unsubscribed user's name.
#: OStatusPlugin.php:606
#: OStatusPlugin.php:604
#, php-format
msgid "%1$s stopped following %2$s."
msgstr ""
#: OStatusPlugin.php:634
#: OStatusPlugin.php:632
msgid "Could not set up remote group membership."
msgstr ""
#. TRANS: Success message for subscribe to group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the subscribed group's name.
#: OStatusPlugin.php:656
#: OStatusPlugin.php:654
#, php-format
msgid "%1$s has joined group %2$s."
msgstr "%1$s a zo bet er strollad %2$s."
#. TRANS: Exception.
#: OStatusPlugin.php:665
#: OStatusPlugin.php:663
msgid "Failed joining remote group."
msgstr ""
#: OStatusPlugin.php:705
#: OStatusPlugin.php:703
msgid "Leave"
msgstr "Kuitaat"
#. TRANS: Success message for unsubscribe from group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the unsubscribed group's name.
#: OStatusPlugin.php:708
#: OStatusPlugin.php:706
#, php-format
msgid "%1$s has left group %2$s."
msgstr ""
#: OStatusPlugin.php:783
#: OStatusPlugin.php:781
msgid "Disfavor"
msgstr ""
#. TRANS: Success message for remove a favorite notice through OStatus.
#. TRANS: %1$s is the unfavoring user's name, %2$s is URI to the no longer favored notice.
#: OStatusPlugin.php:786
#: OStatusPlugin.php:784
#, php-format
msgid "%1$s marked notice %2$s as no longer a favorite."
msgstr ""
#. TRANS: Link text for link to remote subscribe.
#: OStatusPlugin.php:862
#: OStatusPlugin.php:860
msgid "Remote"
msgstr ""
#. TRANS: Title for activity.
#: OStatusPlugin.php:902
#: OStatusPlugin.php:900
msgid "Profile update"
msgstr ""
#. TRANS: Ping text for remote profile update through OStatus.
#. TRANS: %s is user that updated their profile.
#: OStatusPlugin.php:905
#: OStatusPlugin.php:903
#, php-format
msgid "%s has updated their profile page."
msgstr ""
#. TRANS: Plugin description.
#: OStatusPlugin.php:950
#: OStatusPlugin.php:948
msgid ""
"Follow people across social networks that implement <a href=\"http://ostatus."
"org/\">OStatus</a>."
@@ -161,102 +161,102 @@ msgid "RSS feed without a channel."
msgstr ""
#. TRANS: Client exception.
#: classes/Ostatus_profile.php:478
#: classes/Ostatus_profile.php:479
msgid "Can't handle that kind of post."
msgstr ""
#. TRANS: Client exception. %s is a source URI.
#: classes/Ostatus_profile.php:561
#: classes/Ostatus_profile.php:537
#, php-format
msgid "No content for notice %s."
msgstr ""
#. TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
#. TRANS: this will usually be replaced with localised text from StatusNet core messages.
#: classes/Ostatus_profile.php:596
#: classes/Ostatus_profile.php:572
msgid "Show more"
msgstr "Diskouez muioc'h"
#. TRANS: Exception. %s is a profile URL.
#: classes/Ostatus_profile.php:789
#: classes/Ostatus_profile.php:765
#, php-format
msgid "Could not reach profile page %s."
msgstr ""
#. TRANS: Exception. %s is a URL.
#: classes/Ostatus_profile.php:847
#: classes/Ostatus_profile.php:823
#, php-format
msgid "Could not find a feed URL for profile page %s."
msgstr ""
#. TRANS: Feed sub exception.
#: classes/Ostatus_profile.php:985
#: classes/Ostatus_profile.php:922
msgid "Can't find enough profile information to make a feed."
msgstr ""
#. TRANS: Server exception. %s is a URL.
#: classes/Ostatus_profile.php:1045
#: classes/Ostatus_profile.php:986
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""
#. TRANS: Server exception. %s is a URI.
#: classes/Ostatus_profile.php:1056
#: classes/Ostatus_profile.php:997
#, php-format
msgid "Tried to update avatar for unsaved remote profile %s."
msgstr ""
#. TRANS: Server exception. %s is a URL.
#: classes/Ostatus_profile.php:1066
#: classes/Ostatus_profile.php:1007
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1292
#: classes/Ostatus_profile.php:1233
msgid "Local user can't be referenced as remote."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1297
#: classes/Ostatus_profile.php:1238
msgid "Local group can't be referenced as remote."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1349 classes/Ostatus_profile.php:1360
#: classes/Ostatus_profile.php:1290 classes/Ostatus_profile.php:1301
msgid "Can't save local profile."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1368
#: classes/Ostatus_profile.php:1309
msgid "Can't save OStatus profile."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1632 classes/Ostatus_profile.php:1660
#: classes/Ostatus_profile.php:1573 classes/Ostatus_profile.php:1601
msgid "Not a valid webfinger address."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1742
#: classes/Ostatus_profile.php:1683
#, php-format
msgid "Couldn't save profile for \"%s\"."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1761
#: classes/Ostatus_profile.php:1702
#, php-format
msgid "Couldn't save ostatus_profile for \"%s\"."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1769
#: classes/Ostatus_profile.php:1710
#, php-format
msgid "Couldn't find a valid profile for \"%s\"."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1812
#: classes/Ostatus_profile.php:1753
msgid "Could not store HTML content of long post as file."
msgstr ""
@@ -273,72 +273,72 @@ msgid "Callback returned status: %1$s. Body: %2$s"
msgstr ""
#. TRANS: Client error. POST is a HTTP command. It should not be translated.
#: lib/salmonaction.php:42
#: lib/salmonaction.php:43
msgid "This method requires a POST."
msgstr ""
#. TRANS: Client error. Do not translate "application/magic-envelope+xml"
#: lib/salmonaction.php:47
#: lib/salmonaction.php:48
msgid "Salmon requires \"application/magic-envelope+xml\"."
msgstr ""
#. TRANS: Client error.
#: lib/salmonaction.php:57
#: lib/salmonaction.php:58
msgid "Salmon signature verification failed."
msgstr ""
#. TRANS: Client error.
#: lib/salmonaction.php:69
#: lib/salmonaction.php:70
msgid "Salmon post must be an Atom entry."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:118
#: lib/salmonaction.php:120
msgid "Unrecognized activity type."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:127
#: lib/salmonaction.php:130
msgid "This target doesn't understand posts."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:133
#: lib/salmonaction.php:136
msgid "This target doesn't understand follows."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:139
#: lib/salmonaction.php:142
msgid "This target doesn't understand unfollows."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:145
#: lib/salmonaction.php:148
msgid "This target doesn't understand favorites."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:151
#: lib/salmonaction.php:154
msgid "This target doesn't understand unfavorites."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:157
#: lib/salmonaction.php:160
msgid "This target doesn't understand share events."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:163
#: lib/salmonaction.php:166
msgid "This target doesn't understand joins."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:169
#: lib/salmonaction.php:172
msgid "This target doesn't understand leave events."
msgstr ""
#. TRANS: Exception.
#: lib/salmonaction.php:197
#: lib/salmonaction.php:200
msgid "Received a salmon slap from unidentified actor."
msgstr ""
@@ -428,38 +428,38 @@ msgid "No ID."
msgstr "ID ebet"
#. TRANS: Client exception.
#: actions/usersalmon.php:81
#: actions/usersalmon.php:83
msgid "In reply to unknown notice."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:86
#: actions/usersalmon.php:88
msgid "In reply to a notice not by this user and not mentioning this user."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:163
#: actions/usersalmon.php:165
msgid "Could not save new favorite."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:195
#: actions/usersalmon.php:197
msgid "Can't favorite/unfavorite without an object."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:207
#: actions/usersalmon.php:209
msgid "Can't handle that kind of object for liking/faving."
msgstr ""
#. TRANS: Client exception. %s is an object ID.
#: actions/usersalmon.php:214
#: actions/usersalmon.php:216
#, php-format
msgid "Notice with ID %s unknown."
msgstr ""
#. TRANS: Client exception. %1$s is a notice ID, %2$s is a user ID.
#: actions/usersalmon.php:219
#: actions/usersalmon.php:221
#, php-format
msgid "Notice with ID %1$s not posted by %2$s."
msgstr ""
@@ -482,7 +482,7 @@ msgstr "Kenderc'hel"
#: actions/ostatusgroup.php:105
msgid "You are already a member of this group."
msgstr ""
msgstr "Un ezel eus ar strollad-mañ oc'h dija."
#. TRANS: OStatus remote group subscription dialog error.
#: actions/ostatusgroup.php:140
@@ -517,36 +517,36 @@ msgid "No such group."
msgstr "N'eus ket eus ar strollad-se."
#. TRANS: Client error.
#: actions/groupsalmon.php:53
#: actions/groupsalmon.php:56
msgid "Can't accept remote posts for a remote group."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:127
#: actions/groupsalmon.php:130
msgid "Can't read profile to set up group membership."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:131 actions/groupsalmon.php:174
#: actions/groupsalmon.php:134 actions/groupsalmon.php:177
msgid "Groups can't join groups."
msgstr ""
msgstr "Ne c'hell ket strolladoù mont e strolladoù."
#: actions/groupsalmon.php:144
#: actions/groupsalmon.php:147
msgid "You have been blocked from that group by the admin."
msgstr ""
msgstr "Stanket oc'h bet eus ar strollad-mañ gant ur merour."
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#: actions/groupsalmon.php:159
#: actions/groupsalmon.php:162
#, php-format
msgid "Could not join remote user %1$s to group %2$s."
msgstr ""
msgstr "Dibosupl eo distagañ an implijer %1$s deus ar strollad %2$s."
#: actions/groupsalmon.php:171
#: actions/groupsalmon.php:174
msgid "Can't read profile to cancel group membership."
msgstr ""
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#: actions/groupsalmon.php:188
#: actions/groupsalmon.php:191
#, php-format
msgid "Could not remove remote user %1$s from group %2$s."
msgstr ""

View File

@@ -0,0 +1,637 @@
# Translation of StatusNet - OStatus to German (Deutsch)
# Exported from translatewiki.net
#
# Author: Fujnky
# Author: Michael
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - OStatus\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-03-06 02:34+0100\n"
"PO-Revision-Date: 2011-03-06 01:37:59+0000\n"
"Language-Team: German <http://translatewiki.net/wiki/Portal:de>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-03-03 17:49:39+0000\n"
"X-Generator: MediaWiki 1.18alpha (r83348); Translate extension (2011-03-04)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: de\n"
"X-Message-Group: #out-statusnet-plugin-ostatus\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Feeds"
msgstr "Feeds"
#. TRANS: Link description for link to subscribe to a remote user.
#. TRANS: Link text for a user to subscribe to an OStatus user.
msgid "Subscribe"
msgstr "Abonnieren"
#. TRANS: Link description for link to join a remote group.
msgid "Join"
msgstr "Beitreten"
#. TRANSLATE: %s is a domain.
#, php-format
msgid "Sent from %s via OStatus"
msgstr "Gesendet von %s über OStatus"
#. TRANS: Exception.
msgid "Could not set up remote subscription."
msgstr "Konnte Remote-Abonnement nicht einrichten."
msgid "Unfollow"
msgstr "Nicht mehr beachten"
#. TRANS: Success message for unsubscribe from user attempt through OStatus.
#. TRANS: %1$s is the unsubscriber's name, %2$s is the unsubscribed user's name.
#, php-format
msgid "%1$s stopped following %2$s."
msgstr "%1$s folgt %2$s nicht mehr."
msgid "Could not set up remote group membership."
msgstr "Konnte Remotegruppenmitgliedschaft nicht einrichten."
#. TRANS: Success message for subscribe to group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the subscribed group's name.
#, php-format
msgid "%1$s has joined group %2$s."
msgstr "%1$s ist der Gruppe %2$s beigetreten"
#. TRANS: Exception.
msgid "Failed joining remote group."
msgstr "Fehler beim Beitreten der Remotegruppe."
msgid "Leave"
msgstr "Verlassen"
#. TRANS: Success message for unsubscribe from group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the unsubscribed group's name.
#, php-format
msgid "%1$s has left group %2$s."
msgstr "%1$s hat die Gruppe %2$s verlassen"
msgid "Disfavor"
msgstr ""
#. TRANS: Success message for remove a favorite notice through OStatus.
#. TRANS: %1$s is the unfavoring user's name, %2$s is URI to the no longer favored notice.
#, php-format
msgid "%1$s marked notice %2$s as no longer a favorite."
msgstr "%1$s markierte Nachricht %2$s nicht mehr als Favorit."
#. TRANS: Link text for link to remote subscribe.
msgid "Remote"
msgstr ""
#. TRANS: Title for activity.
msgid "Profile update"
msgstr "Profil aktualisieren"
#. TRANS: Ping text for remote profile update through OStatus.
#. TRANS: %s is user that updated their profile.
#, php-format
msgid "%s has updated their profile page."
msgstr "%s hat die Profil-Seite aktualisiert."
#. TRANS: Plugin description.
msgid ""
"Follow people across social networks that implement <a href=\"http://ostatus."
"org/\">OStatus</a>."
msgstr ""
"Folge Leuten quer durch die sozialen Netzwerke, die <a href=\"http://ostatus."
"org/\">OStatus</a> implementieren."
#. TRANS: Client exception.
msgid "Publishing outside feeds not supported."
msgstr "Veröffentlichung von äußeren Feeds nicht unterstützt."
#. TRANS: Client exception. %s is a mode.
#, php-format
msgid "Unrecognized mode \"%s\"."
msgstr "Unbekannter Modus \"%s\"."
#. TRANS: Client exception. %s is a topic.
#, php-format
msgid ""
"Unsupported hub.topic %s this hub only serves local user and group Atom "
"feeds."
msgstr ""
"Nicht unterstütztes hub.topic %s. Dieser Hub stellt nur Atom-Feeds lokaler "
"Benutzer und Gruppen zur verfügung."
#. TRANS: Client exception.
#, php-format
msgid "Invalid hub.verify \"%s\". It must be sync or async."
msgstr "Ungültiger hub.verify „%s“. Es muss sync oder async sein."
#. TRANS: Client exception.
#, php-format
msgid "Invalid hub.lease \"%s\". It must be empty or positive integer."
msgstr ""
"Ungültiger hub.lease „%s“. Es muss eine leere oder positive Ganzzahl sein."
#. TRANS: Client exception.
#, php-format
msgid "Invalid hub.secret \"%s\". It must be under 200 bytes."
msgstr "Ungültiges hub.secret „%s“. Es muss kleiner als 200 Bytes sein."
#. TRANS: Client exception.
#, php-format
msgid "Invalid hub.topic \"%s\". User doesn't exist."
msgstr "Ungültiges hub.topic „%s“. Benutzer existiert nicht."
#. TRANS: Client exception.
#, php-format
msgid "Invalid hub.topic \"%s\". Group doesn't exist."
msgstr "Ungültiges hub.topic „%s“. Gruppe existiert nicht."
#. TRANS: Client exception.
#. TRANS: %1$s is this argument to the method this exception occurs in, %2$s is a URL.
#, php-format
msgid "Invalid URL passed for %1$s: \"%2$s\""
msgstr "Ungültiger URL für %1$s übergeben: „%2$s“"
msgid "Empty or invalid feed id."
msgstr "Leere oder ungültige Feed-ID."
#. TRANS: Server exception. %s is a feed ID.
#, php-format
msgid "Unknown PuSH feed id %s"
msgstr "Unbekannte PuSH Feed-ID %s"
#. TRANS: Client exception. %s is an invalid feed name.
#, php-format
msgid "Bad hub.topic feed \"%s\"."
msgstr "Ungültiger hub.topic-Feed „%s“."
#. TRANS: Client exception. %1$s the invalid token, %2$s is the topic for which the invalid token was given.
#, php-format
msgid "Bad hub.verify_token %1$s for %2$s."
msgstr "Ungültiger hub.verify_token %1$s für %2$s."
#. TRANS: Client exception. %s is an invalid topic.
#, php-format
msgid "Unexpected subscribe request for %s."
msgstr "Unerwartete Deabonnement-Anfrage für %s."
#. TRANS: Client exception. %s is an invalid topic.
#, php-format
msgid "Unexpected unsubscribe request for %s."
msgstr "Unerwartete Deabonnement-Anfrage für %s."
msgid "No such user."
msgstr "Unbekannter Benutzer."
#. TRANS: Field label for a field that takes an OStatus user address.
msgid "Subscribe to"
msgstr "Abonniere"
#. TRANS: Tooltip for field label "Subscribe to".
msgid ""
"OStatus user's address, like nickname@example.com or http://example.net/"
"nickname"
msgstr ""
"Adresse des OStatus-Benutzers, wie nickname@example.com oder http://example."
"net/nickname"
#. TRANS: Button text.
msgctxt "BUTTON"
msgid "Continue"
msgstr "Weiter"
#. TRANS: Button text.
#. TRANS: Tooltip for button "Join".
msgctxt "BUTTON"
msgid "Join this group"
msgstr "Dieser Gruppe beitreten"
#. TRANS: Button text.
msgctxt "BUTTON"
msgid "Confirm"
msgstr "Bestätigen"
#. TRANS: Tooltip for button "Confirm".
msgid "Subscribe to this user"
msgstr "Abonniere diesen Benutzer"
msgid "You are already subscribed to this user."
msgstr "Du hast diesen Benutzer bereits abonniert:"
#. TRANS: Error text.
msgid ""
"Sorry, we could not reach that address. Please make sure that the OStatus "
"address is like nickname@example.com or http://example.net/nickname."
msgstr ""
"Entschuldigung, wir konnte diese Adresse nicht erreichen. Bitte überprüfe, "
"ob die OStatus-Adresse gültig ist. Beispiele: nickname@example.com oder "
"http://example.net/nickname."
#. TRANS: Error text.
msgid ""
"Sorry, we could not reach that feed. Please try that OStatus address again "
"later."
msgstr ""
"Entschuldigung, wir konnten diesen Feed nicht erreichen. Bitte versuche "
"diese OStatus-Adresse später noch einmal."
#. TRANS: OStatus remote subscription dialog error.
msgid "Already subscribed!"
msgstr "Bereits abonniert!"
#. TRANS: OStatus remote subscription dialog error.
msgid "Remote subscription failed!"
msgstr "Remoteabonnement fehlgeschlagen!"
msgid "There was a problem with your session token. Try again, please."
msgstr "Es gab ein Problem mit deinem Sitzungstoken. Bitte versuche es erneut."
#. TRANS: Form title.
msgid "Subscribe to user"
msgstr "Abonniere diesen Benutzer"
#. TRANS: Page title for OStatus remote subscription form
msgid "Confirm"
msgstr "Bestätigen"
#. TRANS: Instructions.
msgid ""
"You can subscribe to users from other supported sites. Paste their address "
"or profile URI below:"
msgstr ""
"Du kannst Benutzer von anderen unterstützten Websites abonnieren. Füge ihre "
"Adresse oder Profil-URI unten ein:"
#. TRANS: Field label.
msgid "Join group"
msgstr "Gruppe beitreten"
#. TRANS: Tooltip for field label "Join group".
msgid "OStatus group's address, like http://example.net/group/nickname."
msgstr ""
"OStatus-Adresse der Gruppe. Beispiel: http://example.net/group/nickname."
msgid "You are already a member of this group."
msgstr "Du bist bereits Mitglied dieser Gruppe."
#. TRANS: OStatus remote group subscription dialog error.
msgid "Already a member!"
msgstr "Bereits Mitglied!"
#. TRANS: OStatus remote group subscription dialog error.
msgid "Remote group join failed!"
msgstr "Beitritt in Remote-Gruppe fehlgeschlagen!"
#. TRANS: OStatus remote group subscription dialog error.
msgid "Remote group join aborted!"
msgstr "Beitritt in Remote-Gruppe abgebrochen!"
#. TRANS: Page title for OStatus remote group join form
msgid "Confirm joining remote group"
msgstr "Bestätige das Beitreten einer Remotegruppe"
#. TRANS: Instructions.
msgid ""
"You can subscribe to groups from other supported sites. Paste the group's "
"profile URI below:"
msgstr ""
"Du kannst Gruppen von anderen unterstützten Websites abonnieren. Füge die "
"URI der Gruppe unten ein:"
#. TRANS: Client error.
msgid "No ID."
msgstr "Keine ID"
#. TRANS: Client exception.
msgid "In reply to unknown notice."
msgstr "In der Antwort auf unbekannte Nachricht."
#. TRANS: Client exception.
msgid "In reply to a notice not by this user and not mentioning this user."
msgstr ""
"In einer Antowrt auf eine Nachricht, die nicht von diesem Benutzer stammt "
"und diesen Benutzer nicht erwähnt."
#. TRANS: Client exception.
msgid "Could not save new favorite."
msgstr "Neuer Favorit konnte nicht gespeichert werden."
#. TRANS: Client exception.
msgid "Can't favorite/unfavorite without an object."
msgstr "Kann nicht ohne Objekt (ent)favorisieren."
#. TRANS: Client exception.
msgid "Can't handle that kind of object for liking/faving."
msgstr "Kann diese Art von Objekt nicht für mögen/favorisieren verarbeiten."
#. TRANS: Client exception. %s is an object ID.
#, php-format
msgid "Notice with ID %s unknown."
msgstr "Nachricht mit ID %s unbekannt."
#. TRANS: Client exception. %1$s is a notice ID, %2$s is a user ID.
#, php-format
msgid "Notice with ID %1$s not posted by %2$s."
msgstr "Nachricht mit ID %1$s wurde nicht von %2$s geschrieben."
#. TRANS: Client error.
msgid "No such group."
msgstr "Keine derartige Gruppe."
#. TRANS: Client error.
msgid "Can't accept remote posts for a remote group."
msgstr "Kann Remoteposts für Remotegruppen nicht akzeptieren."
#. TRANS: Client error.
msgid "Can't read profile to set up group membership."
msgstr "Kann Profil nicht lesen, um die Gruppenmitgliedschaft einzurichten."
#. TRANS: Client error.
msgid "Groups can't join groups."
msgstr "Gruppen können Remotegruppen nicht beitreten."
msgid "You have been blocked from that group by the admin."
msgstr "Der Admin dieser Gruppe hat dich blockiert."
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#, php-format
msgid "Could not join remote user %1$s to group %2$s."
msgstr "Konnte Remotebenutzer %1$s nicht der Gruppe %2$s hinzufügen."
msgid "Can't read profile to cancel group membership."
msgstr "Kann Profil nicht lesen, um die Gruppenmitgliedschaft zu kündigen."
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#, php-format
msgid "Could not remove remote user %1$s from group %2$s."
msgstr "Konnte Remotebenutzer %1$s nicht aus der Gruppe %2$s entfernen."
#. TRANS: Client error.
msgid "You can use the local subscription!"
msgstr "Du kannst ein lokales Abonnement erstellen!"
#. TRANS: Form legend.
#, php-format
msgid "Join group %s"
msgstr "Gruppe %s beitreten"
#. TRANS: Button text.
msgctxt "BUTTON"
msgid "Join"
msgstr "Beitreten"
#. TRANS: Form legend.
#, php-format
msgid "Subscribe to %s"
msgstr "Abonniere %s"
#. TRANS: Button text.
msgctxt "BUTTON"
msgid "Subscribe"
msgstr "Abonnieren"
#. TRANS: Field label.
msgid "Group nickname"
msgstr "Gruppe-Nickname"
msgid "Nickname of the group you want to join."
msgstr "Spitzname der Gruppe, der Sie beitreten möchten."
#. TRANS: Field label.
msgid "User nickname"
msgstr "Benutzername"
msgid "Nickname of the user you want to follow."
msgstr "Name des Benutzers, dem du folgen möchtest"
#. TRANS: Field label.
msgid "Profile Account"
msgstr "Profil-Konto"
#. TRANS: Tooltip for field label "Profile Account".
msgid "Your account id (e.g. user@identi.ca)."
msgstr "Deine Konto-ID (z.B. user@identi.ca)."
#. TRANS: Client error.
msgid "Must provide a remote profile."
msgstr "Du musst ein Remoteprofil angeben."
#. TRANS: Client error.
msgid "Couldn't look up OStatus account profile."
msgstr "Konnte OStatus-Konto-Profil nicht nachschauen."
#. TRANS: Client error.
msgid "Couldn't confirm remote profile address."
msgstr "Konnte Remoteprofiladresse nicht bestätigen."
#. TRANS: Page title.
msgid "OStatus Connect"
msgstr "OStatus-Verbindung"
msgid "Attempting to start PuSH subscription for feed with no hub."
msgstr ""
"Es wird versucht, ein PuSH-Abonnemont für einen Feed ohne Hub zu starten."
msgid "Attempting to end PuSH subscription for feed with no hub."
msgstr ""
"Es wird versucht, ein PuSH-Abonnemont für einen Feed ohne Hub zu beenden."
#. TRANS: Server exception. %s is a URI.
#, php-format
msgid "Invalid ostatus_profile state: both group and profile IDs set for %s."
msgstr ""
"Ungültiger ostatus_profile-Zustand: Sowohl Gruppen- als auch Profil-ID für %"
"s gesetzt."
#. TRANS: Server exception. %s is a URI.
#, php-format
msgid "Invalid ostatus_profile state: both group and profile IDs empty for %s."
msgstr ""
"Ungültiger ostatus_profile-Zustand: Sowohl Gruppen- als auch Profil-ID für %"
"s sind leer."
#. TRANS: Server exception.
#. TRANS: %1$s is the method name the exception occured in, %2$s is the actor type.
#, php-format
msgid "Invalid actor passed to %1$s: %2$s."
msgstr "Ungültiger actor an %1$s übergeben: %2$s."
#. TRANS: Server exception.
msgid ""
"Invalid type passed to Ostatus_profile::notify. It must be XML string or "
"Activity entry."
msgstr ""
"Ungültiger Typ an Ostatus_profile::notify übergeben. Es muss ein XML-String "
"oder ein Activity-Eintrag sein."
#. TRANS: Exception.
msgid "Unknown feed format."
msgstr "Unbekanntes Feed-Format."
#. TRANS: Exception.
msgid "RSS feed without a channel."
msgstr "RSS-Feed ohne einen Kanal."
#. TRANS: Client exception.
msgid "Can't handle that kind of post."
msgstr "Kann diese Art von Post nicht verarbeiten."
#. TRANS: Client exception. %s is a source URI.
#, php-format
msgid "No content for notice %s."
msgstr "Kein Inhalt für Nachricht %s."
#. TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
#. TRANS: this will usually be replaced with localised text from StatusNet core messages.
msgid "Show more"
msgstr "Mehr anzeigen"
#. TRANS: Exception. %s is a profile URL.
#, php-format
msgid "Could not reach profile page %s."
msgstr "Konnte Profilseite %s nicht erreichen."
#. TRANS: Exception. %s is a URL.
#, php-format
msgid "Could not find a feed URL for profile page %s."
msgstr "Konnte keinen Feed-URL für die Profilseite %s finden."
#. TRANS: Feed sub exception.
msgid "Can't find enough profile information to make a feed."
msgstr ""
"Kann nicht genug Profilinformationen finden, um einen Feed zu erstellen."
#. TRANS: Server exception. %s is a URL.
#, php-format
msgid "Invalid avatar URL %s."
msgstr "Ungültiger Avatar-URL %s."
#. TRANS: Server exception. %s is a URI.
#, php-format
msgid "Tried to update avatar for unsaved remote profile %s."
msgstr ""
"Versuchte den Avatar für ein ungespeichertes Remoteprofil %s zu "
"aktualisieren."
#. TRANS: Server exception. %s is a URL.
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr "Kann den Avatar von %s nicht abrufen."
#. TRANS: Exception.
msgid "Local user can't be referenced as remote."
msgstr "Lokaler Benutzer kann nicht als remote verwiesen werden."
#. TRANS: Exception.
msgid "Local group can't be referenced as remote."
msgstr "Lokale Gruppe kann nicht als remote verwiesen werden."
#. TRANS: Server exception.
msgid "Can't save local profile."
msgstr "Lokales Profil kann nicht gespeichert werden."
#. TRANS: Server exception.
msgid "Can't save OStatus profile."
msgstr "OStatus-Profil kann nicht gespeichert werden."
#. TRANS: Exception.
msgid "Not a valid webfinger address."
msgstr "Ungültige Webfinger-Adresse."
#. TRANS: Exception. %s is a webfinger address.
#, php-format
msgid "Couldn't save profile for \"%s\"."
msgstr "Profil für „%s“ konnte nicht gespeichert werden."
#. TRANS: Exception. %s is a webfinger address.
#, php-format
msgid "Couldn't save ostatus_profile for \"%s\"."
msgstr "Ostatus_profile für „%s“ konnte nicht gespeichert werden."
#. TRANS: Exception. %s is a webfinger address.
#, php-format
msgid "Couldn't find a valid profile for \"%s\"."
msgstr "Es konnte kein gültiges Profil für „%s“ gefunden werden."
#. TRANS: Server exception.
msgid "Could not store HTML content of long post as file."
msgstr ""
"HTML-Inhalt eines langen Posts konnte nicht als Datei nicht gespeichert "
"werden."
#. TRANS: Client exception. %s is a HTTP status code.
#, php-format
msgid "Hub subscriber verification returned HTTP %s."
msgstr "Hub-Abonnenten-Überprüfung gab HTTP %s zurück."
#. TRANS: Exception. %1$s is a response status code, %2$s is the body of the response.
#, php-format
msgid "Callback returned status: %1$s. Body: %2$s"
msgstr "Der Aufruf gab folgenden Status zurück: %1$s. Body: %2$s"
#. TRANS: Exception.
msgid "Unable to locate signer public key."
msgstr "Konnte den öffentlichen Schlüssel des Unterzeichners nicht finden."
#. TRANS: Exception.
msgid "Salmon invalid actor for signing."
msgstr ""
#. TRANS: Client error. POST is a HTTP command. It should not be translated.
msgid "This method requires a POST."
msgstr "Diese Methode benötigt ein POST."
#. TRANS: Client error. Do not translate "application/magic-envelope+xml"
msgid "Salmon requires \"application/magic-envelope+xml\"."
msgstr "Salmon erfordert „application/magic-envelope+xml“."
#. TRANS: Client error.
msgid "Salmon signature verification failed."
msgstr "Salmon-Signaturpfüung fehlgeschlagen."
#. TRANS: Client error.
msgid "Salmon post must be an Atom entry."
msgstr "Salmon-Post muss ein Atom-Eintrag sein."
#. TRANS: Client exception.
msgid "Unrecognized activity type."
msgstr "Unbekannter Aktivitätstyp."
#. TRANS: Client exception.
msgid "This target doesn't understand posts."
msgstr "Dieses Ziel versteht keine Posts."
#. TRANS: Client exception.
msgid "This target doesn't understand follows."
msgstr "Dieses Ziel versteht keine Follows."
#. TRANS: Client exception.
msgid "This target doesn't understand unfollows."
msgstr "Dieses Ziel versteht keine Unfollows."
#. TRANS: Client exception.
msgid "This target doesn't understand favorites."
msgstr "Dieses Ziel versteht keine Favoriten."
#. TRANS: Client exception.
msgid "This target doesn't understand unfavorites."
msgstr "Dieses Ziel versteht keine Unfavorites."
#. TRANS: Client exception.
msgid "This target doesn't understand share events."
msgstr "Dieses Ziel versteht das Teilen von Events nicht."
#. TRANS: Client exception.
msgid "This target doesn't understand joins."
msgstr "Dieses Ziel versteht keine Joins."
#. TRANS: Client exception.
msgid "This target doesn't understand leave events."
msgstr "Dieses Ziel versteht das Verlassen von Events nicht."
#. TRANS: Exception.
msgid "Received a salmon slap from unidentified actor."
msgstr "Einen Salmon-Slap von einem unidentifizierten Aktor empfangen."

File diff suppressed because it is too large Load Diff

View File

@@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - OStatus\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-12-16 15:08+0000\n"
"PO-Revision-Date: 2010-12-16 15:12:44+0000\n"
"POT-Creation-Date: 2011-01-14 10:29+0000\n"
"PO-Revision-Date: 2011-01-14 10:33:50+0000\n"
"Language-Team: Galician <http://translatewiki.net/wiki/Portal:gl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2010-11-30 20:43:51+0000\n"
"X-Generator: MediaWiki 1.18alpha (r78478); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2011-01-10 18:26:06+0000\n"
"X-Generator: MediaWiki 1.18alpha (r80246); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: gl\n"
"X-Message-Group: #out-statusnet-plugin-ostatus\n"
@@ -23,94 +23,94 @@ msgstr ""
#. TRANS: Link description for link to subscribe to a remote user.
#. TRANS: Link text for a user to subscribe to an OStatus user.
#: OStatusPlugin.php:225 OStatusPlugin.php:935
#: OStatusPlugin.php:223 OStatusPlugin.php:933
msgid "Subscribe"
msgstr "Subscribirse"
#. TRANS: Link description for link to join a remote group.
#: OStatusPlugin.php:244 OStatusPlugin.php:653 actions/ostatussub.php:109
#: OStatusPlugin.php:242 OStatusPlugin.php:651 actions/ostatussub.php:109
msgid "Join"
msgstr "Unirse"
#. TRANSLATE: %s is a domain.
#: OStatusPlugin.php:457
#: OStatusPlugin.php:455
#, php-format
msgid "Sent from %s via OStatus"
msgstr ""
#. TRANS: Exception.
#: OStatusPlugin.php:529
#: OStatusPlugin.php:527
msgid "Could not set up remote subscription."
msgstr ""
#: OStatusPlugin.php:603
#: OStatusPlugin.php:601
msgid "Unfollow"
msgstr ""
#. TRANS: Success message for unsubscribe from user attempt through OStatus.
#. TRANS: %1$s is the unsubscriber's name, %2$s is the unsubscribed user's name.
#: OStatusPlugin.php:606
#: OStatusPlugin.php:604
#, php-format
msgid "%1$s stopped following %2$s."
msgstr ""
#: OStatusPlugin.php:634
#: OStatusPlugin.php:632
msgid "Could not set up remote group membership."
msgstr ""
#. TRANS: Success message for subscribe to group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the subscribed group's name.
#: OStatusPlugin.php:656
#: OStatusPlugin.php:654
#, php-format
msgid "%1$s has joined group %2$s."
msgstr ""
#. TRANS: Exception.
#: OStatusPlugin.php:665
#: OStatusPlugin.php:663
msgid "Failed joining remote group."
msgstr ""
#: OStatusPlugin.php:705
#: OStatusPlugin.php:703
msgid "Leave"
msgstr "Deixar"
#. TRANS: Success message for unsubscribe from group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the unsubscribed group's name.
#: OStatusPlugin.php:708
#: OStatusPlugin.php:706
#, php-format
msgid "%1$s has left group %2$s."
msgstr ""
#: OStatusPlugin.php:783
#: OStatusPlugin.php:781
msgid "Disfavor"
msgstr ""
#. TRANS: Success message for remove a favorite notice through OStatus.
#. TRANS: %1$s is the unfavoring user's name, %2$s is URI to the no longer favored notice.
#: OStatusPlugin.php:786
#: OStatusPlugin.php:784
#, php-format
msgid "%1$s marked notice %2$s as no longer a favorite."
msgstr ""
#. TRANS: Link text for link to remote subscribe.
#: OStatusPlugin.php:862
#: OStatusPlugin.php:860
msgid "Remote"
msgstr ""
#. TRANS: Title for activity.
#: OStatusPlugin.php:902
#: OStatusPlugin.php:900
msgid "Profile update"
msgstr "Actualización do perfil"
#. TRANS: Ping text for remote profile update through OStatus.
#. TRANS: %s is user that updated their profile.
#: OStatusPlugin.php:905
#: OStatusPlugin.php:903
#, php-format
msgid "%s has updated their profile page."
msgstr ""
#. TRANS: Plugin description.
#: OStatusPlugin.php:950
#: OStatusPlugin.php:948
msgid ""
"Follow people across social networks that implement <a href=\"http://ostatus."
"org/\">OStatus</a>."
@@ -161,102 +161,102 @@ msgid "RSS feed without a channel."
msgstr ""
#. TRANS: Client exception.
#: classes/Ostatus_profile.php:478
#: classes/Ostatus_profile.php:479
msgid "Can't handle that kind of post."
msgstr ""
#. TRANS: Client exception. %s is a source URI.
#: classes/Ostatus_profile.php:561
#: classes/Ostatus_profile.php:537
#, php-format
msgid "No content for notice %s."
msgstr ""
#. TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
#. TRANS: this will usually be replaced with localised text from StatusNet core messages.
#: classes/Ostatus_profile.php:596
#: classes/Ostatus_profile.php:572
msgid "Show more"
msgstr "Mostrar máis"
#. TRANS: Exception. %s is a profile URL.
#: classes/Ostatus_profile.php:789
#: classes/Ostatus_profile.php:765
#, php-format
msgid "Could not reach profile page %s."
msgstr ""
#. TRANS: Exception. %s is a URL.
#: classes/Ostatus_profile.php:847
#: classes/Ostatus_profile.php:823
#, php-format
msgid "Could not find a feed URL for profile page %s."
msgstr ""
#. TRANS: Feed sub exception.
#: classes/Ostatus_profile.php:985
#: classes/Ostatus_profile.php:922
msgid "Can't find enough profile information to make a feed."
msgstr ""
#. TRANS: Server exception. %s is a URL.
#: classes/Ostatus_profile.php:1045
#: classes/Ostatus_profile.php:986
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""
#. TRANS: Server exception. %s is a URI.
#: classes/Ostatus_profile.php:1056
#: classes/Ostatus_profile.php:997
#, php-format
msgid "Tried to update avatar for unsaved remote profile %s."
msgstr ""
#. TRANS: Server exception. %s is a URL.
#: classes/Ostatus_profile.php:1066
#: classes/Ostatus_profile.php:1007
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1292
#: classes/Ostatus_profile.php:1233
msgid "Local user can't be referenced as remote."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1297
#: classes/Ostatus_profile.php:1238
msgid "Local group can't be referenced as remote."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1349 classes/Ostatus_profile.php:1360
#: classes/Ostatus_profile.php:1290 classes/Ostatus_profile.php:1301
msgid "Can't save local profile."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1368
#: classes/Ostatus_profile.php:1309
msgid "Can't save OStatus profile."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1632 classes/Ostatus_profile.php:1660
#: classes/Ostatus_profile.php:1573 classes/Ostatus_profile.php:1601
msgid "Not a valid webfinger address."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1742
#: classes/Ostatus_profile.php:1683
#, php-format
msgid "Couldn't save profile for \"%s\"."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1761
#: classes/Ostatus_profile.php:1702
#, php-format
msgid "Couldn't save ostatus_profile for \"%s\"."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1769
#: classes/Ostatus_profile.php:1710
#, php-format
msgid "Couldn't find a valid profile for \"%s\"."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1812
#: classes/Ostatus_profile.php:1753
msgid "Could not store HTML content of long post as file."
msgstr ""
@@ -273,72 +273,72 @@ msgid "Callback returned status: %1$s. Body: %2$s"
msgstr ""
#. TRANS: Client error. POST is a HTTP command. It should not be translated.
#: lib/salmonaction.php:42
#: lib/salmonaction.php:43
msgid "This method requires a POST."
msgstr ""
#. TRANS: Client error. Do not translate "application/magic-envelope+xml"
#: lib/salmonaction.php:47
#: lib/salmonaction.php:48
msgid "Salmon requires \"application/magic-envelope+xml\"."
msgstr ""
#. TRANS: Client error.
#: lib/salmonaction.php:57
#: lib/salmonaction.php:58
msgid "Salmon signature verification failed."
msgstr ""
#. TRANS: Client error.
#: lib/salmonaction.php:69
#: lib/salmonaction.php:70
msgid "Salmon post must be an Atom entry."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:118
#: lib/salmonaction.php:120
msgid "Unrecognized activity type."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:127
#: lib/salmonaction.php:130
msgid "This target doesn't understand posts."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:133
#: lib/salmonaction.php:136
msgid "This target doesn't understand follows."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:139
#: lib/salmonaction.php:142
msgid "This target doesn't understand unfollows."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:145
#: lib/salmonaction.php:148
msgid "This target doesn't understand favorites."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:151
#: lib/salmonaction.php:154
msgid "This target doesn't understand unfavorites."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:157
#: lib/salmonaction.php:160
msgid "This target doesn't understand share events."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:163
#: lib/salmonaction.php:166
msgid "This target doesn't understand joins."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:169
#: lib/salmonaction.php:172
msgid "This target doesn't understand leave events."
msgstr ""
#. TRANS: Exception.
#: lib/salmonaction.php:197
#: lib/salmonaction.php:200
msgid "Received a salmon slap from unidentified actor."
msgstr ""
@@ -428,38 +428,38 @@ msgid "No ID."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:81
#: actions/usersalmon.php:83
msgid "In reply to unknown notice."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:86
#: actions/usersalmon.php:88
msgid "In reply to a notice not by this user and not mentioning this user."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:163
#: actions/usersalmon.php:165
msgid "Could not save new favorite."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:195
#: actions/usersalmon.php:197
msgid "Can't favorite/unfavorite without an object."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:207
#: actions/usersalmon.php:209
msgid "Can't handle that kind of object for liking/faving."
msgstr ""
#. TRANS: Client exception. %s is an object ID.
#: actions/usersalmon.php:214
#: actions/usersalmon.php:216
#, php-format
msgid "Notice with ID %s unknown."
msgstr ""
#. TRANS: Client exception. %1$s is a notice ID, %2$s is a user ID.
#: actions/usersalmon.php:219
#: actions/usersalmon.php:221
#, php-format
msgid "Notice with ID %1$s not posted by %2$s."
msgstr ""
@@ -517,36 +517,36 @@ msgid "No such group."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:53
#: actions/groupsalmon.php:56
msgid "Can't accept remote posts for a remote group."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:127
#: actions/groupsalmon.php:130
msgid "Can't read profile to set up group membership."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:131 actions/groupsalmon.php:174
#: actions/groupsalmon.php:134 actions/groupsalmon.php:177
msgid "Groups can't join groups."
msgstr ""
#: actions/groupsalmon.php:144
#: actions/groupsalmon.php:147
msgid "You have been blocked from that group by the admin."
msgstr ""
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#: actions/groupsalmon.php:159
#: actions/groupsalmon.php:162
#, php-format
msgid "Could not join remote user %1$s to group %2$s."
msgstr ""
#: actions/groupsalmon.php:171
#: actions/groupsalmon.php:174
msgid "Can't read profile to cancel group membership."
msgstr ""
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#: actions/groupsalmon.php:188
#: actions/groupsalmon.php:191
#, php-format
msgid "Could not remove remote user %1$s from group %2$s."
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,758 @@
# Translation of StatusNet - OStatus to Simplified Chinese (‪中文(简体))
# Expored from translatewiki.net
#
# Author: ZhengYiFeng
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - OStatus\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-15 00:20+0000\n"
"PO-Revision-Date: 2011-01-15 00:24:30+0000\n"
"Language-Team: Simplified Chinese <http://translatewiki.net/wiki/Portal:zh-"
"hans>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-01-14 13:21:17+0000\n"
"X-Generator: MediaWiki 1.18alpha (r80364); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: zh-hans\n"
"X-Message-Group: #out-statusnet-plugin-ostatus\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#. TRANS: Link description for link to subscribe to a remote user.
#. TRANS: Link text for a user to subscribe to an OStatus user.
#: OStatusPlugin.php:223 OStatusPlugin.php:933
msgid "Subscribe"
msgstr "关注"
#. TRANS: Link description for link to join a remote group.
#: OStatusPlugin.php:242 OStatusPlugin.php:651 actions/ostatussub.php:109
msgid "Join"
msgstr "加入"
#. TRANSLATE: %s is a domain.
#: OStatusPlugin.php:455
#, php-format
msgid "Sent from %s via OStatus"
msgstr "从 %s 通过 OStatus 发布"
#. TRANS: Exception.
#: OStatusPlugin.php:527
msgid "Could not set up remote subscription."
msgstr "无法设置远程关注。"
#: OStatusPlugin.php:601
msgid "Unfollow"
msgstr "取消关注"
#. TRANS: Success message for unsubscribe from user attempt through OStatus.
#. TRANS: %1$s is the unsubscriber's name, %2$s is the unsubscribed user's name.
#: OStatusPlugin.php:604
#, php-format
msgid "%1$s stopped following %2$s."
msgstr "%1$s 取消了对 %2$s 的关注。"
#: OStatusPlugin.php:632
msgid "Could not set up remote group membership."
msgstr "无法设置远程的小组成员。"
#. TRANS: Success message for subscribe to group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the subscribed group's name.
#: OStatusPlugin.php:654
#, php-format
msgid "%1$s has joined group %2$s."
msgstr ""
#. TRANS: Exception.
#: OStatusPlugin.php:663
msgid "Failed joining remote group."
msgstr "加入远程小组失败。"
#: OStatusPlugin.php:703
msgid "Leave"
msgstr "离开"
#. TRANS: Success message for unsubscribe from group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the unsubscribed group's name.
#: OStatusPlugin.php:706
#, php-format
msgid "%1$s has left group %2$s."
msgstr ""
#: OStatusPlugin.php:781
msgid "Disfavor"
msgstr "取消收藏"
#. TRANS: Success message for remove a favorite notice through OStatus.
#. TRANS: %1$s is the unfavoring user's name, %2$s is URI to the no longer favored notice.
#: OStatusPlugin.php:784
#, php-format
msgid "%1$s marked notice %2$s as no longer a favorite."
msgstr "%1$s 将消息 %2$s 取消了收藏。"
#. TRANS: Link text for link to remote subscribe.
#: OStatusPlugin.php:860
msgid "Remote"
msgstr "远程"
#. TRANS: Title for activity.
#: OStatusPlugin.php:900
msgid "Profile update"
msgstr "个人信息更新"
#. TRANS: Ping text for remote profile update through OStatus.
#. TRANS: %s is user that updated their profile.
#: OStatusPlugin.php:903
#, php-format
msgid "%s has updated their profile page."
msgstr "%s 更新了他/她的个人信息页。"
#. TRANS: Plugin description.
#: OStatusPlugin.php:948
msgid ""
"Follow people across social networks that implement <a href=\"http://ostatus."
"org/\">OStatus</a>."
msgstr ""
#: classes/FeedSub.php:252
msgid "Attempting to start PuSH subscription for feed with no hub."
msgstr ""
#: classes/FeedSub.php:282
msgid "Attempting to end PuSH subscription for feed with no hub."
msgstr ""
#. TRANS: Server exception. %s is a URI.
#: classes/Ostatus_profile.php:192
#, php-format
msgid "Invalid ostatus_profile state: both group and profile IDs set for %s."
msgstr ""
#. TRANS: Server exception. %s is a URI.
#: classes/Ostatus_profile.php:195
#, php-format
msgid "Invalid ostatus_profile state: both group and profile IDs empty for %s."
msgstr ""
#. TRANS: Server exception.
#. TRANS: %1$s is the method name the exception occured in, %2$s is the actor type.
#: classes/Ostatus_profile.php:285
#, php-format
msgid "Invalid actor passed to %1$s: %2$s."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:378
msgid ""
"Invalid type passed to Ostatus_profile::notify. It must be XML string or "
"Activity entry."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:409
msgid "Unknown feed format."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:433
msgid "RSS feed without a channel."
msgstr ""
#. TRANS: Client exception.
#: classes/Ostatus_profile.php:479
msgid "Can't handle that kind of post."
msgstr ""
#. TRANS: Client exception. %s is a source URI.
#: classes/Ostatus_profile.php:537
#, php-format
msgid "No content for notice %s."
msgstr ""
#. TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
#. TRANS: this will usually be replaced with localised text from StatusNet core messages.
#: classes/Ostatus_profile.php:572
msgid "Show more"
msgstr ""
#. TRANS: Exception. %s is a profile URL.
#: classes/Ostatus_profile.php:765
#, php-format
msgid "Could not reach profile page %s."
msgstr ""
#. TRANS: Exception. %s is a URL.
#: classes/Ostatus_profile.php:823
#, php-format
msgid "Could not find a feed URL for profile page %s."
msgstr ""
#. TRANS: Feed sub exception.
#: classes/Ostatus_profile.php:922
msgid "Can't find enough profile information to make a feed."
msgstr ""
#. TRANS: Server exception. %s is a URL.
#: classes/Ostatus_profile.php:986
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""
#. TRANS: Server exception. %s is a URI.
#: classes/Ostatus_profile.php:997
#, php-format
msgid "Tried to update avatar for unsaved remote profile %s."
msgstr ""
#. TRANS: Server exception. %s is a URL.
#: classes/Ostatus_profile.php:1007
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1233
msgid "Local user can't be referenced as remote."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1238
msgid "Local group can't be referenced as remote."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1290 classes/Ostatus_profile.php:1301
msgid "Can't save local profile."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1309
msgid "Can't save OStatus profile."
msgstr ""
#. TRANS: Exception.
#: classes/Ostatus_profile.php:1573 classes/Ostatus_profile.php:1601
msgid "Not a valid webfinger address."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1683
#, php-format
msgid "Couldn't save profile for \"%s\"."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1702
#, php-format
msgid "Couldn't save ostatus_profile for \"%s\"."
msgstr ""
#. TRANS: Exception. %s is a webfinger address.
#: classes/Ostatus_profile.php:1710
#, php-format
msgid "Couldn't find a valid profile for \"%s\"."
msgstr ""
#. TRANS: Server exception.
#: classes/Ostatus_profile.php:1753
msgid "Could not store HTML content of long post as file."
msgstr ""
#. TRANS: Client exception. %s is a HTTP status code.
#: classes/HubSub.php:212
#, php-format
msgid "Hub subscriber verification returned HTTP %s."
msgstr ""
#. TRANS: Exception. %1$s is a response status code, %2$s is the body of the response.
#: classes/HubSub.php:359
#, php-format
msgid "Callback returned status: %1$s. Body: %2$s"
msgstr ""
#. TRANS: Client error. POST is a HTTP command. It should not be translated.
#: lib/salmonaction.php:43
msgid "This method requires a POST."
msgstr ""
#. TRANS: Client error. Do not translate "application/magic-envelope+xml"
#: lib/salmonaction.php:48
msgid "Salmon requires \"application/magic-envelope+xml\"."
msgstr ""
#. TRANS: Client error.
#: lib/salmonaction.php:58
msgid "Salmon signature verification failed."
msgstr ""
#. TRANS: Client error.
#: lib/salmonaction.php:70
msgid "Salmon post must be an Atom entry."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:120
msgid "Unrecognized activity type."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:130
msgid "This target doesn't understand posts."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:136
msgid "This target doesn't understand follows."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:142
msgid "This target doesn't understand unfollows."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:148
msgid "This target doesn't understand favorites."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:154
msgid "This target doesn't understand unfavorites."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:160
msgid "This target doesn't understand share events."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:166
msgid "This target doesn't understand joins."
msgstr ""
#. TRANS: Client exception.
#: lib/salmonaction.php:172
msgid "This target doesn't understand leave events."
msgstr ""
#. TRANS: Exception.
#: lib/salmonaction.php:200
msgid "Received a salmon slap from unidentified actor."
msgstr ""
#. TRANS: Exception.
#: lib/discovery.php:110
#, php-format
msgid "Unable to find services for %s."
msgstr ""
#. TRANS: Exception.
#: lib/magicenvelope.php:80
msgid "Unable to locate signer public key."
msgstr ""
#. TRANS: Exception.
#: lib/salmon.php:93
msgid "Salmon invalid actor for signing."
msgstr ""
#: tests/gettext-speedtest.php:57
msgid "Feeds"
msgstr ""
#. TRANS: Client exception.
#: actions/pushhub.php:70
msgid "Publishing outside feeds not supported."
msgstr ""
#. TRANS: Client exception. %s is a mode.
#: actions/pushhub.php:73
#, php-format
msgid "Unrecognized mode \"%s\"."
msgstr ""
#. TRANS: Client exception. %s is a topic.
#: actions/pushhub.php:93
#, php-format
msgid ""
"Unsupported hub.topic %s this hub only serves local user and group Atom "
"feeds."
msgstr ""
#. TRANS: Client exception.
#: actions/pushhub.php:99
#, php-format
msgid "Invalid hub.verify \"%s\". It must be sync or async."
msgstr ""
#. TRANS: Client exception.
#: actions/pushhub.php:105
#, php-format
msgid "Invalid hub.lease \"%s\". It must be empty or positive integer."
msgstr ""
#. TRANS: Client exception.
#: actions/pushhub.php:113
#, php-format
msgid "Invalid hub.secret \"%s\". It must be under 200 bytes."
msgstr ""
#. TRANS: Client exception.
#: actions/pushhub.php:165
#, php-format
msgid "Invalid hub.topic \"%s\". User doesn't exist."
msgstr ""
#. TRANS: Client exception.
#: actions/pushhub.php:174
#, php-format
msgid "Invalid hub.topic \"%s\". Group doesn't exist."
msgstr ""
#. TRANS: Client exception.
#. TRANS: %1$s is this argument to the method this exception occurs in, %2$s is a URL.
#: actions/pushhub.php:199
#, php-format
msgid "Invalid URL passed for %1$s: \"%2$s\""
msgstr ""
#: actions/ownerxrd.php:39 actions/usersalmon.php:43
msgid "No such user."
msgstr ""
#. TRANS: Client error.
#: actions/usersalmon.php:37 actions/groupsalmon.php:40
msgid "No ID."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:83
msgid "In reply to unknown notice."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:88
msgid "In reply to a notice not by this user and not mentioning this user."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:165
msgid "Could not save new favorite."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:197
msgid "Can't favorite/unfavorite without an object."
msgstr ""
#. TRANS: Client exception.
#: actions/usersalmon.php:209
msgid "Can't handle that kind of object for liking/faving."
msgstr ""
#. TRANS: Client exception. %s is an object ID.
#: actions/usersalmon.php:216
#, php-format
msgid "Notice with ID %s unknown."
msgstr ""
#. TRANS: Client exception. %1$s is a notice ID, %2$s is a user ID.
#: actions/usersalmon.php:221
#, php-format
msgid "Notice with ID %1$s not posted by %2$s."
msgstr ""
#. TRANS: Field label.
#: actions/ostatusgroup.php:78
msgid "Join group"
msgstr "加入小组"
#. TRANS: Tooltip for field label "Join group".
#: actions/ostatusgroup.php:81
msgid "OStatus group's address, like http://example.net/group/nickname."
msgstr "OStatus 小组的地址,例如 http://example.net/group/nickname。"
#. TRANS: Button text.
#: actions/ostatusgroup.php:86 actions/ostatussub.php:75
msgctxt "BUTTON"
msgid "Continue"
msgstr "继续"
#: actions/ostatusgroup.php:105
msgid "You are already a member of this group."
msgstr "你已经是该小组成员。"
#. TRANS: OStatus remote group subscription dialog error.
#: actions/ostatusgroup.php:140
msgid "Already a member!"
msgstr "已经是成员了!"
#. TRANS: OStatus remote group subscription dialog error.
#: actions/ostatusgroup.php:151
msgid "Remote group join failed!"
msgstr "加入远程小组失败!"
#. TRANS: OStatus remote group subscription dialog error.
#: actions/ostatusgroup.php:155
msgid "Remote group join aborted!"
msgstr "加入远程小组取消!"
#. TRANS: Page title for OStatus remote group join form
#: actions/ostatusgroup.php:167
msgid "Confirm joining remote group"
msgstr "确认加入远程小组"
#. TRANS: Instructions.
#: actions/ostatusgroup.php:178
msgid ""
"You can subscribe to groups from other supported sites. Paste the group's "
"profile URI below:"
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:47
msgid "No such group."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:56
msgid "Can't accept remote posts for a remote group."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:130
msgid "Can't read profile to set up group membership."
msgstr ""
#. TRANS: Client error.
#: actions/groupsalmon.php:134 actions/groupsalmon.php:177
msgid "Groups can't join groups."
msgstr ""
#: actions/groupsalmon.php:147
msgid "You have been blocked from that group by the admin."
msgstr ""
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#: actions/groupsalmon.php:162
#, php-format
msgid "Could not join remote user %1$s to group %2$s."
msgstr ""
#: actions/groupsalmon.php:174
msgid "Can't read profile to cancel group membership."
msgstr ""
#. TRANS: Server error. %1$s is a profile URI, %2$s is a group nickname.
#: actions/groupsalmon.php:191
#, php-format
msgid "Could not remove remote user %1$s from group %2$s."
msgstr ""
#. TRANS: Field label for a field that takes an OStatus user address.
#: actions/ostatussub.php:68
msgid "Subscribe to"
msgstr ""
#. TRANS: Tooltip for field label "Subscribe to".
#: actions/ostatussub.php:71
msgid ""
"OStatus user's address, like nickname@example.com or http://example.net/"
"nickname"
msgstr ""
"OStatus 用户的地址,例如 nickname@example.com 或 http://example.net/nickname"
#. TRANS: Button text.
#. TRANS: Tooltip for button "Join".
#: actions/ostatussub.php:112
msgctxt "BUTTON"
msgid "Join this group"
msgstr ""
#. TRANS: Button text.
#: actions/ostatussub.php:115
msgctxt "BUTTON"
msgid "Confirm"
msgstr ""
#. TRANS: Tooltip for button "Confirm".
#: actions/ostatussub.php:117
msgid "Subscribe to this user"
msgstr ""
#: actions/ostatussub.php:138
msgid "You are already subscribed to this user."
msgstr ""
#: actions/ostatussub.php:167
msgid "Photo"
msgstr ""
#: actions/ostatussub.php:178
msgid "Nickname"
msgstr ""
#: actions/ostatussub.php:199
msgid "Location"
msgstr ""
#: actions/ostatussub.php:208
msgid "URL"
msgstr ""
#: actions/ostatussub.php:220
msgid "Note"
msgstr ""
#. TRANS: Error text.
#: actions/ostatussub.php:256 actions/ostatussub.php:263
#: actions/ostatussub.php:288
msgid ""
"Sorry, we could not reach that address. Please make sure that the OStatus "
"address is like nickname@example.com or http://example.net/nickname."
msgstr ""
#. TRANS: Error text.
#: actions/ostatussub.php:267 actions/ostatussub.php:271
#: actions/ostatussub.php:275 actions/ostatussub.php:279
#: actions/ostatussub.php:283
msgid ""
"Sorry, we could not reach that feed. Please try that OStatus address again "
"later."
msgstr ""
#. TRANS: OStatus remote subscription dialog error.
#: actions/ostatussub.php:317
msgid "Already subscribed!"
msgstr ""
#. TRANS: OStatus remote subscription dialog error.
#: actions/ostatussub.php:322
msgid "Remote subscription failed!"
msgstr ""
#: actions/ostatussub.php:369 actions/ostatusinit.php:64
msgid "There was a problem with your session token. Try again, please."
msgstr ""
#. TRANS: Form title.
#: actions/ostatussub.php:397 actions/ostatusinit.php:83
msgid "Subscribe to user"
msgstr ""
#. TRANS: Page title for OStatus remote subscription form
#: actions/ostatussub.php:417
msgid "Confirm"
msgstr ""
#. TRANS: Instructions.
#: actions/ostatussub.php:429
msgid ""
"You can subscribe to users from other supported sites. Paste their address "
"or profile URI below:"
msgstr ""
#. TRANS: Client error.
#: actions/ostatusinit.php:42
msgid "You can use the local subscription!"
msgstr ""
#. TRANS: Form legend.
#: actions/ostatusinit.php:98
#, php-format
msgid "Join group %s"
msgstr ""
#. TRANS: Button text.
#: actions/ostatusinit.php:100
msgctxt "BUTTON"
msgid "Join"
msgstr ""
#. TRANS: Form legend.
#: actions/ostatusinit.php:103
#, php-format
msgid "Subscribe to %s"
msgstr ""
#. TRANS: Button text.
#: actions/ostatusinit.php:105
msgctxt "BUTTON"
msgid "Subscribe"
msgstr ""
#. TRANS: Field label.
#: actions/ostatusinit.php:118
msgid "User nickname"
msgstr ""
#: actions/ostatusinit.php:119
msgid "Nickname of the user you want to follow."
msgstr ""
#. TRANS: Field label.
#: actions/ostatusinit.php:124
msgid "Profile Account"
msgstr ""
#. TRANS: Tooltip for field label "Profile Account".
#: actions/ostatusinit.php:126
msgid "Your account id (e.g. user@identi.ca)."
msgstr ""
#. TRANS: Client error.
#: actions/ostatusinit.php:148
msgid "Must provide a remote profile."
msgstr ""
#. TRANS: Client error.
#: actions/ostatusinit.php:160
msgid "Couldn't look up OStatus account profile."
msgstr ""
#. TRANS: Client error.
#: actions/ostatusinit.php:173
msgid "Couldn't confirm remote profile address."
msgstr ""
#. TRANS: Page title.
#: actions/ostatusinit.php:218
msgid "OStatus Connect"
msgstr ""
#: actions/pushcallback.php:50
msgid "Empty or invalid feed id."
msgstr ""
#. TRANS: Server exception. %s is a feed ID.
#: actions/pushcallback.php:56
#, php-format
msgid "Unknown PuSH feed id %s"
msgstr ""
#. TRANS: Client exception. %s is an invalid feed name.
#: actions/pushcallback.php:96
#, php-format
msgid "Bad hub.topic feed \"%s\"."
msgstr ""
#. TRANS: Client exception. %1$s the invalid token, %2$s is the topic for which the invalid token was given.
#: actions/pushcallback.php:101
#, php-format
msgid "Bad hub.verify_token %1$s for %2$s."
msgstr ""
#. TRANS: Client exception. %s is an invalid topic.
#: actions/pushcallback.php:108
#, php-format
msgid "Unexpected subscribe request for %s."
msgstr ""
#. TRANS: Client exception. %s is an invalid topic.
#: actions/pushcallback.php:113
#, php-format
msgid "Unexpected unsubscribe request for %s."
msgstr ""

View File

@@ -0,0 +1,121 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
$longoptions = array('all', 'suspicious', 'quiet');
$helptext = <<<END_OF_HELP
update-profile-data.php [options] [http://example.com/profile/url]
Rerun profile discovery for the given OStatus remote profile, and save the
updated profile data (nickname, avatar, bio, etc). Doesn't touch feed state.
Can be used to clean up after breakages.
Options:
--all Run for all known OStatus profiles
--suspicious Run for OStatus profiles with all-numeric nicknames
(fixes 0.9.7 prerelease back-compatibility bug)
END_OF_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
function showProfileInfo($oprofile) {
if ($oprofile->isGroup()) {
echo "group\n";
} else {
$profile = $oprofile->localProfile();
foreach (array('nickname', 'bio', 'homepage', 'location') as $field) {
print " $field: {$profile->$field}\n";
}
}
echo "\n";
}
function fixProfile($uri) {
$oprofile = Ostatus_profile::staticGet('uri', $uri);
if (!$oprofile) {
print "No OStatus remote profile known for URI $uri\n";
return false;
}
echo "Before:\n";
showProfileInfo($oprofile);
$feedurl = $oprofile->feeduri;
$client = new HttpClient();
$response = $client->get($feedurl);
if ($response->isOk()) {
echo "Updating profile from feed: $feedurl\n";
$dom = new DOMDocument();
if ($dom->loadXML($response->getBody())) {
$feed = $dom->documentElement;
$entries = $dom->getElementsByTagNameNS(Activity::ATOM, 'entry');
if ($entries->length) {
$entry = $entries->item(0);
$activity = new Activity($entry, $feed);
$oprofile->checkAuthorship($activity);
echo " (ok)\n";
} else {
echo " (no entry; skipping)\n";
return false;
}
} else {
echo " (bad feed; skipping)\n";
return false;
}
} else {
echo "Failed feed fetch: {$response->getStatus()} for $feedurl\n";
return false;
}
echo "After:\n";
showProfileInfo($oprofile);
return true;
}
$ok = true;
if (have_option('all')) {
$oprofile = new Ostatus_profile();
$oprofile->find();
echo "Found $oprofile->N profiles:\n\n";
while ($oprofile->fetch()) {
$ok = fixProfile($oprofile->uri) && $ok;
}
} else if (have_option('suspicious')) {
$oprofile = new Ostatus_profile();
$oprofile->joinAdd(array('profile_id', 'profile:id'));
$oprofile->whereAdd("nickname rlike '^[0-9]$'");
$oprofile->find();
echo "Found $oprofile->N matching profiles:\n\n";
while ($oprofile->fetch()) {
$ok = fixProfile($oprofile->uri) && $ok;
}
} else if (!empty($args[0]) && Validate::uri($args[0])) {
$uri = $args[0];
$ok = fixProfile($uri);
} else {
print "$helptext";
$ok = false;
}
exit($ok ? 0 : 1);

View File

@@ -0,0 +1,60 @@
<?php
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
print "This script must be run from the command line\n";
exit();
}
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
define('STATUSNET', true);
require_once INSTALLDIR . '/lib/common.php';
class MagicEnvelopeTest extends PHPUnit_Framework_TestCase
{
/**
* Test that MagicEnvelope builds the correct plaintext for signing.
* @dataProvider provider
*/
public function testSignatureText($env, $expected)
{
$magic = new MagicEnvelope;
$text = $magic->signingText($env);
$this->assertEquals($expected, $text, "'$text' should be '$expected'");
}
static public function provider()
{
return array(
array(
// Sample case given in spec:
// http://salmon-protocol.googlecode.com/svn/trunk/draft-panzer-magicsig-00.html#signing
array(
'data' => 'Tm90IHJlYWxseSBBdG9t',
'data_type' => 'application/atom+xml',
'encoding' => 'base64url',
'alg' => 'RSA-SHA256'
),
'Tm90IHJlYWxseSBBdG9t.YXBwbGljYXRpb24vYXRvbSt4bWw=.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='
)
);
}
/**
* Test that MagicEnvelope builds the correct plaintext for signing.
* @dataProvider provider
*/
public function testSignatureTextCompat($env, $expected)
{
// Our old code didn't add the extra fields, just used the armored text.
$alt = $env['data'];
$magic = new MagicEnvelopeCompat;
$text = $magic->signingText($env);
$this->assertEquals($alt, $text, "'$text' should be '$alt'");
}
}

View File

@@ -72,6 +72,8 @@ class OStatusTester extends TestBase
$base = 'test' . mt_rand(1, 1000000);
$this->pub = new SNTestClient($this->a, 'pub' . $base, 'pw-' . mt_rand(1, 1000000), $timeout);
$this->sub = new SNTestClient($this->b, 'sub' . $base, 'pw-' . mt_rand(1, 1000000), $timeout);
$this->group = 'group' . $base;
}
function run()
@@ -163,6 +165,39 @@ class OStatusTester extends TestBase
$this->assertFalse($this->pub->hasSubscriber($this->sub->getProfileUri()));
}
function testCreateGroup()
{
$this->groupUrl = $this->pub->createGroup($this->group);
$this->assertTrue(!empty($this->groupUrl));
}
function testJoinGroup()
{
#$this->assertFalse($this->sub->inGroup($this->groupUrl));
$this->sub->joinGroup($this->groupUrl);
#$this->assertTrue($this->sub->inGroup($this->groupUrl));
}
function testLocalGroupPost()
{
$post = $this->pub->post("Group post from local to !{$this->group}, should go out over push.");
$this->assertNotEqual('', $post);
$this->sub->assertReceived($post);
}
function testRemoteGroupPost()
{
$post = $this->sub->post("Group post from remote to !{$this->group}, should come in over salmon.");
$this->assertNotEqual('', $post);
$this->pub->assertReceived($post);
}
function testLeaveGroup()
{
#$this->assertTrue($this->sub->inGroup($this->groupUrl));
$this->sub->leaveGroup($this->groupUrl);
#$this->assertFalse($this->sub->inGroup($this->groupUrl));
}
}
class SNTestClient extends TestBase
@@ -534,6 +569,63 @@ class SNTestClient extends TestBase
return false;
}
/**
* Create a group on this site.
*
* @param string $nickname
* @param array $options
* @return string: profile URL for the group
*/
function createGroup($nickname, $options=array()) {
$this->log("Creating group as %s on %s: %s",
$this->username,
$this->basepath,
$nickname);
$data = $this->api('statusnet/groups/create', 'json',
array_merge(array('nickname' => $nickname), $options));
$url = $data['url'];
if ($url) {
$this->log(' created as %s', $url);
} else {
$this->log(' failed? %s', var_export($data, true));
}
return $url;
}
function groupInfo($nickname) {
$data = $this->api('statusnet/groups/show', 'json', array(
'id' => $nickname
));
}
/**
* Join a group.
*
* @param string $group nickname or URL
*/
function joinGroup($group) {
$this->post('join ' . $group);
}
/**
* Leave a group.
*
* @param string $group nickname or URL
*/
function leaveGroup($group) {
$this->post('drop ' . $group);
}
/**
*
* @param string $nickname
* @return
*/
function inGroup($nickname) {
// @todo
}
}
// @fixme switch to commandline.inc?

View File

@@ -0,0 +1,92 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
$longoptions = array('verify', 'slap=', 'notice=');
$helptext = <<<END_OF_HELP
slap.php [options]
Test generation and sending of magic envelopes for Salmon slaps.
--notice=N generate entry for this notice number
--verify send signed magic envelope to Tuomas Koski's test service
--slap=<url> send signed Salmon slap to the destination endpoint
END_OF_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
if (!have_option('--notice')) {
print "$helptext";
exit(1);
}
$notice_id = get_option_value('--notice');
$notice = Notice::staticGet('id', $notice_id);
$profile = $notice->getProfile();
$entry = $notice->asAtomEntry(true);
echo "== Original entry ==\n\n";
print $entry;
print "\n\n";
$salmon = new Salmon();
$envelope = $salmon->createMagicEnv($entry, $profile);
echo "== Signed envelope ==\n\n";
print $envelope;
print "\n\n";
echo "== Testing local verification ==\n\n";
$ok = $salmon->verifyMagicEnv($envelope);
if ($ok) {
print "OK\n\n";
} else {
print "FAIL\n\n";
}
if (have_option('--verify')) {
$url = 'http://www.madebymonsieur.com/ostatus_discovery/magic_env/validate/';
echo "== Testing remote verification ==\n\n";
print "Sending for verification to $url ...\n";
$client = new HTTPClient();
$response = $client->post($url, array(), array('magic_env' => $envelope));
print $response->getStatus() . "\n\n";
print $response->getBody() . "\n\n";
}
if (have_option('--slap')) {
$url = get_option_value('--slap');
echo "== Remote salmon slap ==\n\n";
print "Sending signed Salmon slap to $url ...\n";
$ok = $salmon->post($url, $entry, $profile);
if ($ok) {
print "OK\n\n";
} else {
print "FAIL\n\n";
}
}