Merge remote branch 'gitorious/testing' into testing
This commit is contained in:
		@@ -66,6 +66,12 @@ class ApiGroupMembershipAction extends ApiPrivateAuthAction
 | 
				
			|||||||
        parent::prepare($args);
 | 
					        parent::prepare($args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->group    = $this->getTargetGroup($this->arg('id'));
 | 
					        $this->group    = $this->getTargetGroup($this->arg('id'));
 | 
				
			||||||
 | 
					        if (empty($this->group)) {
 | 
				
			||||||
 | 
					            // TRANS: Client error displayed trying to show group membership on a non-existing group.
 | 
				
			||||||
 | 
					            $this->clientError(_('Group not found.'), 404, $this->format);
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->profiles = $this->getProfiles();
 | 
					        $this->profiles = $this->getProfiles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
@@ -84,12 +90,6 @@ class ApiGroupMembershipAction extends ApiPrivateAuthAction
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        parent::handle($args);
 | 
					        parent::handle($args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (empty($this->group)) {
 | 
					 | 
				
			||||||
            // TRANS: Client error displayed trying to show group membership on a non-existing group.
 | 
					 | 
				
			||||||
            $this->clientError(_('Group not found.'), 404, $this->format);
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // XXX: RSS and Atom
 | 
					        // XXX: RSS and Atom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch($this->format) {
 | 
					        switch($this->format) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -377,7 +377,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
 | 
				
			|||||||
    function supported($cmd)
 | 
					    function supported($cmd)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        static $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand',
 | 
					        static $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand',
 | 
				
			||||||
            'FavCommand', 'OnCommand', 'OffCommand');
 | 
					            'FavCommand', 'OnCommand', 'OffCommand', 'JoinCommand', 'LeaveCommand');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (in_array(get_class($cmd), $cmdlist)) {
 | 
					        if (in_array(get_class($cmd), $cmdlist)) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -200,8 +200,6 @@ class NewgroupAction extends Action
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $mainpage = common_local_url('showgroup', array('nickname' => $nickname));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $cur = common_current_user();
 | 
					        $cur = common_current_user();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Checked in prepare() above
 | 
					        // Checked in prepare() above
 | 
				
			||||||
@@ -215,7 +213,6 @@ class NewgroupAction extends Action
 | 
				
			|||||||
                                            'location' => $location,
 | 
					                                            'location' => $location,
 | 
				
			||||||
                                            'aliases'  => $aliases,
 | 
					                                            'aliases'  => $aliases,
 | 
				
			||||||
                                            'userid'   => $cur->id,
 | 
					                                            'userid'   => $cur->id,
 | 
				
			||||||
                                            'mainpage' => $mainpage,
 | 
					 | 
				
			||||||
                                            'local'    => true));
 | 
					                                            'local'    => true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        common_redirect($group->homeUrl(), 303);
 | 
					        common_redirect($group->homeUrl(), 303);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,8 +100,6 @@ class SubscribersAction extends GalleryAction
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $subscribers->free();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
 | 
					        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
 | 
				
			||||||
                          $this->page, 'subscribers',
 | 
					                          $this->page, 'subscribers',
 | 
				
			||||||
                          array('nickname' => $this->user->nickname));
 | 
					                          array('nickname' => $this->user->nickname));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,8 +106,6 @@ class SubscriptionsAction extends GalleryAction
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $subscriptions->free();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
 | 
					            $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
 | 
				
			||||||
                              $this->page, 'subscriptions',
 | 
					                              $this->page, 'subscriptions',
 | 
				
			||||||
                              array('nickname' => $this->user->nickname));
 | 
					                              array('nickname' => $this->user->nickname));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -487,6 +487,7 @@ class User_group extends Memcached_DataObject
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // MAGICALLY put fields into current scope
 | 
					        // MAGICALLY put fields into current scope
 | 
				
			||||||
 | 
					        // @fixme kill extract(); it makes debugging absurdly hard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        extract($fields);
 | 
					        extract($fields);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -498,6 +499,9 @@ class User_group extends Memcached_DataObject
 | 
				
			|||||||
            // fill in later...
 | 
					            // fill in later...
 | 
				
			||||||
            $uri = null;
 | 
					            $uri = null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (empty($mainpage)) {
 | 
				
			||||||
 | 
					            $mainpage = common_local_url('showgroup', array('nickname' => $nickname));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $group->nickname    = $nickname;
 | 
					        $group->nickname    = $nickname;
 | 
				
			||||||
        $group->fullname    = $fullname;
 | 
					        $group->fullname    = $fullname;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -182,6 +182,9 @@ class Activity
 | 
				
			|||||||
        $actorEl = $this->_child($entry, self::ACTOR);
 | 
					        $actorEl = $this->_child($entry, self::ACTOR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!empty($actorEl)) {
 | 
					        if (!empty($actorEl)) {
 | 
				
			||||||
 | 
					            // Standalone <activity:actor> elements are a holdover from older
 | 
				
			||||||
 | 
					            // versions of ActivityStreams. Newer feeds should have this data
 | 
				
			||||||
 | 
					            // integrated straight into <atom:author>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->actor = new ActivityObject($actorEl);
 | 
					            $this->actor = new ActivityObject($actorEl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -196,19 +199,24 @@ class Activity
 | 
				
			|||||||
                    $this->actor->id = $authorObj->id;
 | 
					                    $this->actor->id = $authorObj->id;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else if (!empty($feed) &&
 | 
					 | 
				
			||||||
                   $subjectEl = $this->_child($feed, self::SUBJECT)) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $this->actor = new ActivityObject($subjectEl);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        } else if ($authorEl = $this->_child($entry, self::AUTHOR, self::ATOM)) {
 | 
					        } else if ($authorEl = $this->_child($entry, self::AUTHOR, self::ATOM)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // An <atom:author> in the entry overrides any author info on
 | 
				
			||||||
 | 
					            // the surrounding feed.
 | 
				
			||||||
            $this->actor = new ActivityObject($authorEl);
 | 
					            $this->actor = new ActivityObject($authorEl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } else if (!empty($feed) && $authorEl = $this->_child($feed, self::AUTHOR,
 | 
					        } else if (!empty($feed) && $authorEl = $this->_child($feed, self::AUTHOR,
 | 
				
			||||||
                                                              self::ATOM)) {
 | 
					                                                              self::ATOM)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // If there's no <atom:author> on the entry, it's safe to assume
 | 
				
			||||||
 | 
					            // the containing feed's authorship info applies.
 | 
				
			||||||
            $this->actor = new ActivityObject($authorEl);
 | 
					            $this->actor = new ActivityObject($authorEl);
 | 
				
			||||||
 | 
					        } else if (!empty($feed) &&
 | 
				
			||||||
 | 
					                   $subjectEl = $this->_child($feed, self::SUBJECT)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Feed subject is used for things like groups.
 | 
				
			||||||
 | 
					            // Should actually possibly not be interpreted as an actor...?
 | 
				
			||||||
 | 
					            $this->actor = new ActivityObject($subjectEl);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $contextEl = $this->_child($entry, self::CONTEXT);
 | 
					        $contextEl = $this->_child($entry, self::CONTEXT);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1437,41 +1437,23 @@ class ApiAction extends Action
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        if (empty($id)) {
 | 
					        if (empty($id)) {
 | 
				
			||||||
            if (self::is_decimal($this->arg('id'))) {
 | 
					            if (self::is_decimal($this->arg('id'))) {
 | 
				
			||||||
                return User_group::staticGet($this->arg('id'));
 | 
					                return User_group::staticGet('id', $this->arg('id'));
 | 
				
			||||||
            } else if ($this->arg('id')) {
 | 
					            } else if ($this->arg('id')) {
 | 
				
			||||||
                $nickname = common_canonical_nickname($this->arg('id'));
 | 
					                return User_group::getForNickname($this->arg('id'));
 | 
				
			||||||
                $local = Local_group::staticGet('nickname', $nickname);
 | 
					 | 
				
			||||||
                if (empty($local)) {
 | 
					 | 
				
			||||||
                    return null;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    return User_group::staticGet('id', $local->id);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else if ($this->arg('group_id')) {
 | 
					            } else if ($this->arg('group_id')) {
 | 
				
			||||||
                // This is to ensure that a non-numeric user_id still
 | 
					                // This is to ensure that a non-numeric group_id still
 | 
				
			||||||
                // overrides screen_name even if it doesn't get used
 | 
					                // overrides group_name even if it doesn't get used
 | 
				
			||||||
                if (self::is_decimal($this->arg('group_id'))) {
 | 
					                if (self::is_decimal($this->arg('group_id'))) {
 | 
				
			||||||
                    return User_group::staticGet('id', $this->arg('group_id'));
 | 
					                    return User_group::staticGet('id', $this->arg('group_id'));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else if ($this->arg('group_name')) {
 | 
					            } else if ($this->arg('group_name')) {
 | 
				
			||||||
                $nickname = common_canonical_nickname($this->arg('group_name'));
 | 
					                return User_group::getForNickname($this->arg('group_name'));
 | 
				
			||||||
                $local = Local_group::staticGet('nickname', $nickname);
 | 
					 | 
				
			||||||
                if (empty($local)) {
 | 
					 | 
				
			||||||
                    return null;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    return User_group::staticGet('id', $local->group_id);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } else if (self::is_decimal($id)) {
 | 
					        } else if (self::is_decimal($id)) {
 | 
				
			||||||
            return User_group::staticGet($id);
 | 
					            return User_group::staticGet('id', $id);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $nickname = common_canonical_nickname($id);
 | 
					            return User_group::getForNickname($id);
 | 
				
			||||||
            $local = Local_group::staticGet('nickname', $nickname);
 | 
					 | 
				
			||||||
            if (empty($local)) {
 | 
					 | 
				
			||||||
                return null;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                return User_group::staticGet('id', $local->group_id);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,6 +76,10 @@ class ArrayWrapper
 | 
				
			|||||||
    function __call($name, $args)
 | 
					    function __call($name, $args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $item =& $this->_items[$this->_i];
 | 
					        $item =& $this->_items[$this->_i];
 | 
				
			||||||
 | 
					        if (!is_object($item)) {
 | 
				
			||||||
 | 
					            common_log(LOG_ERR, "Invalid entry " . var_export($item, true) . " at index $this->_i of $this->N; calling $name()");
 | 
				
			||||||
 | 
					            throw new ServerException("Internal error: bad entry in array wrapper list.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        return call_user_func_array(array($item, $name), $args);
 | 
					        return call_user_func_array(array($item, $name), $args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,7 +95,11 @@ function _have_config()
 | 
				
			|||||||
    return StatusNet::haveConfig();
 | 
					    return StatusNet::haveConfig();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function __autoload($cls)
 | 
					/**
 | 
				
			||||||
 | 
					 * Wrapper for class autoloaders.
 | 
				
			||||||
 | 
					 * This used to be the special function name __autoload(), but that causes bugs with PHPUnit 3.5+
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function autoload_sn($cls)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
 | 
					    if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
 | 
				
			||||||
        require_once(INSTALLDIR.'/classes/' . $cls . '.php');
 | 
					        require_once(INSTALLDIR.'/classes/' . $cls . '.php');
 | 
				
			||||||
@@ -111,6 +115,8 @@ function __autoload($cls)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					spl_autoload_register('autoload_sn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// XXX: how many of these could be auto-loaded on use?
 | 
					// XXX: how many of these could be auto-loaded on use?
 | 
				
			||||||
// XXX: note that these files should not use config options
 | 
					// XXX: note that these files should not use config options
 | 
				
			||||||
// at compile time since DB config options are not yet loaded.
 | 
					// at compile time since DB config options are not yet loaded.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -121,7 +121,7 @@ function mail_notify_from()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $domain = mail_domain();
 | 
					        $domain = mail_domain();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $notifyfrom = '"'.common_config('site', 'name') .'" <noreply@'.$domain.'>';
 | 
					        $notifyfrom = '"'. str_replace('"', '\\"', common_config('site', 'name')) .'" <noreply@'.$domain.'>';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return $notifyfrom;
 | 
					    return $notifyfrom;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,12 @@ jQuery(document).ready(function($){
 | 
				
			|||||||
    contentSelector : "#notices_primary ol.notices",
 | 
					    contentSelector : "#notices_primary ol.notices",
 | 
				
			||||||
    itemSelector    : "#notices_primary ol.notices li"
 | 
					    itemSelector    : "#notices_primary ol.notices li"
 | 
				
			||||||
    },function(){
 | 
					    },function(){
 | 
				
			||||||
        SN.Init.Notices();
 | 
					        // Reply button and attachment magic need to be set up
 | 
				
			||||||
 | 
					        // for each new notice.
 | 
				
			||||||
 | 
					        // DO NOT run SN.Init.Notices() which will duplicate stuff.
 | 
				
			||||||
 | 
					        $(this).find('.notice').each(function() {
 | 
				
			||||||
 | 
					            SN.U.NoticeReplyTo($(this));
 | 
				
			||||||
 | 
					            SN.U.NoticeWithAttachment($(this));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@
 | 
				
			|||||||
    // grab each selector option and see if any fail.
 | 
					    // grab each selector option and see if any fail.
 | 
				
			||||||
    function areSelectorsValid(opts){
 | 
					    function areSelectorsValid(opts){
 | 
				
			||||||
      for (var key in opts){
 | 
					      for (var key in opts){
 | 
				
			||||||
        if (key.indexOf && key.indexOf('Selector') && $(opts[key]).length === 0){
 | 
					        if (key.indexOf && (key.indexOf('Selector') != -1) && $(opts[key]).length === 0){
 | 
				
			||||||
            debug('Your ' + key + ' found no elements.');    
 | 
					            debug('Your ' + key + ' found no elements.');    
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        } 
 | 
					        } 
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,11 +39,41 @@ class Magicsig extends Memcached_DataObject
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public $__table = 'magicsig';
 | 
					    public $__table = 'magicsig';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Key to user.id/profile.id for the local user whose key we're storing.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public $user_id;
 | 
					    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;
 | 
					    public $keypair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Crypto algorithm used for this key; currently only RSA-SHA256 is supported.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public $alg;
 | 
					    public $alg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Public RSA key; gets serialized in/out via $this->keypair string.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var Crypt_RSA
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public $publicKey;
 | 
					    public $publicKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * PrivateRSA key; gets serialized in/out via $this->keypair string.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var Crypt_RSA
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public $privateKey;
 | 
					    public $privateKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct($alg = 'RSA-SHA256')
 | 
					    public function __construct($alg = 'RSA-SHA256')
 | 
				
			||||||
@@ -51,6 +81,13 @@ class Magicsig extends Memcached_DataObject
 | 
				
			|||||||
        $this->alg = $alg;
 | 
					        $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)
 | 
					    public /*static*/ function staticGet($k, $v=null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $obj =  parent::staticGet(__CLASS__, $k, $v);
 | 
					        $obj =  parent::staticGet(__CLASS__, $k, $v);
 | 
				
			||||||
@@ -103,6 +140,14 @@ class Magicsig extends Memcached_DataObject
 | 
				
			|||||||
        return array(false, false, false);
 | 
					        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()
 | 
					    function insert()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->keypair = $this->toString();
 | 
					        $this->keypair = $this->toString();
 | 
				
			||||||
@@ -110,6 +155,14 @@ class Magicsig extends Memcached_DataObject
 | 
				
			|||||||
        return parent::insert();
 | 
					        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)
 | 
					    public function generate($user_id)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $rsa = new Crypt_RSA();
 | 
					        $rsa = new Crypt_RSA();
 | 
				
			||||||
@@ -128,6 +181,12 @@ class Magicsig extends Memcached_DataObject
 | 
				
			|||||||
        $this->insert();
 | 
					        $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)
 | 
					    public function toString($full_pair = true)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $mod = Magicsig::base64_url_encode($this->publicKey->modulus->toBytes());
 | 
					        $mod = Magicsig::base64_url_encode($this->publicKey->modulus->toBytes());
 | 
				
			||||||
@@ -140,6 +199,13 @@ class Magicsig extends Memcached_DataObject
 | 
				
			|||||||
        return 'RSA.' . $mod . '.' . $exp . $private_exp;
 | 
					        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)
 | 
					    public static function fromString($text)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $magic_sig = new Magicsig();
 | 
					        $magic_sig = new Magicsig();
 | 
				
			||||||
@@ -168,6 +234,14 @@ class Magicsig extends Memcached_DataObject
 | 
				
			|||||||
        return $magic_sig;
 | 
					        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')
 | 
					    public function loadKey($mod, $exp, $type = 'public')
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        common_log(LOG_DEBUG, "Adding ".$type." key: (".$mod .', '. $exp .")");
 | 
					        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()
 | 
					    public function getName()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->alg;
 | 
					        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()
 | 
					    public function getHash()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        switch ($this->alg) {
 | 
					        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)
 | 
					    public function sign($bytes)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $sig = $this->privateKey->sign($bytes);
 | 
					        $sig = $this->privateKey->sign($bytes);
 | 
				
			||||||
        return Magicsig::base64_url_encode($sig);
 | 
					        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)
 | 
					    public function verify($signed_bytes, $signature)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $signature = Magicsig::base64_url_decode($signature);
 | 
					        $signature = Magicsig::base64_url_decode($signature);
 | 
				
			||||||
        return $this->publicKey->verify($signed_bytes, $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)
 | 
					    public static function base64_url_encode($input)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return strtr(base64_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)
 | 
					    public static function base64_url_decode($input)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return base64_decode(strtr($input, '-_', '+/'));
 | 
					        return base64_decode(strtr($input, '-_', '+/'));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -331,6 +331,7 @@ class Ostatus_profile extends Memcached_DataObject
 | 
				
			|||||||
     * an acceptable response from the remote site.
 | 
					     * an acceptable response from the remote site.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param mixed $entry XML string, Notice, or Activity
 | 
					     * @param mixed $entry XML string, Notice, or Activity
 | 
				
			||||||
 | 
					     * @param Profile $actor
 | 
				
			||||||
     * @return boolean success
 | 
					     * @return boolean success
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function notifyActivity($entry, $actor)
 | 
					    public function notifyActivity($entry, $actor)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,21 +80,53 @@ class MagicEnvelope
 | 
				
			|||||||
        throw new Exception(_m('Unable to locate signer public key.'));
 | 
					        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)
 | 
					    public function signMessage($text, $mimetype, $keypair)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $signature_alg = Magicsig::fromString($keypair);
 | 
					        $signature_alg = Magicsig::fromString($keypair);
 | 
				
			||||||
        $armored_text = Magicsig::base64_url_encode($text);
 | 
					        $armored_text = Magicsig::base64_url_encode($text);
 | 
				
			||||||
 | 
					        $env = array(
 | 
				
			||||||
        return array(
 | 
					 | 
				
			||||||
            'data' => $armored_text,
 | 
					            'data' => $armored_text,
 | 
				
			||||||
            'encoding' => MagicEnvelope::ENCODING,
 | 
					            'encoding' => MagicEnvelope::ENCODING,
 | 
				
			||||||
            'data_type' => $mimetype,
 | 
					            'data_type' => $mimetype,
 | 
				
			||||||
            'sig' => $signature_alg->sign($armored_text),
 | 
					            'sig' => '',
 | 
				
			||||||
            'alg' => $signature_alg->getName()
 | 
					            '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) {
 | 
					    public function toXML($env) {
 | 
				
			||||||
        $xs = new XMLStringer();
 | 
					        $xs = new XMLStringer();
 | 
				
			||||||
        $xs->startXML();
 | 
					        $xs->startXML();
 | 
				
			||||||
@@ -110,6 +142,16 @@ class MagicEnvelope
 | 
				
			|||||||
        return $string;
 | 
					        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)
 | 
					    public function unfold($env)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $dom = new DOMDocument();
 | 
					        $dom = new DOMDocument();
 | 
				
			||||||
@@ -136,6 +178,14 @@ class MagicEnvelope
 | 
				
			|||||||
        return $dom->saveXML();
 | 
					        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) {
 | 
					    public function getAuthor($text) {
 | 
				
			||||||
        $doc = new DOMDocument();
 | 
					        $doc = new DOMDocument();
 | 
				
			||||||
        if (!$doc->loadXML($text)) {
 | 
					        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)
 | 
					    public function checkAuthor($text, $signer_uri)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return ($this->getAuthor($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)
 | 
					    public function verify($env)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($env['alg'] != 'RSA-SHA256') {
 | 
					        if ($env['alg'] != 'RSA-SHA256') {
 | 
				
			||||||
@@ -187,15 +256,35 @@ class MagicEnvelope
 | 
				
			|||||||
            return false;
 | 
					            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)
 | 
					    public function parse($text)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $dom = DOMDocument::loadXML($text);
 | 
					        $dom = DOMDocument::loadXML($text);
 | 
				
			||||||
        return $this->fromDom($dom);
 | 
					        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)
 | 
					    public function fromDom($dom)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $env_element = $dom->getElementsByTagNameNS(MagicEnvelope::NS, 'env')->item(0);
 | 
					        $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'];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,10 +38,12 @@ class Salmon
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Sign and post the given Atom entry as a Salmon message.
 | 
					     * 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 $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
 | 
					     * @return boolean success
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function post($endpoint_uri, $xml, $actor)
 | 
					    public function post($endpoint_uri, $xml, $actor)
 | 
				
			||||||
@@ -50,8 +52,9 @@ class Salmon
 | 
				
			|||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($this->formatClasses() as $class) {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
            $xml = $this->createMagicEnv($xml, $actor);
 | 
					                $envelope = $this->createMagicEnv($xml, $actor, $class);
 | 
				
			||||||
            } catch (Exception $e) {
 | 
					            } catch (Exception $e) {
 | 
				
			||||||
                common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
 | 
					                common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
@@ -61,23 +64,53 @@ class Salmon
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                $client = new HTTPClient();
 | 
					                $client = new HTTPClient();
 | 
				
			||||||
            $client->setBody($xml);
 | 
					                $client->setBody($envelope);
 | 
				
			||||||
                $response = $client->post($endpoint_uri, $headers);
 | 
					                $response = $client->post($endpoint_uri, $headers);
 | 
				
			||||||
            } catch (HTTP_Request2_Exception $e) {
 | 
					            } catch (HTTP_Request2_Exception $e) {
 | 
				
			||||||
            common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage());
 | 
					                common_log(LOG_ERR, "Salmon ($class) post to $endpoint_uri failed: " . $e->getMessage());
 | 
				
			||||||
            return false;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if ($response->getStatus() != 200) {
 | 
					            if ($response->getStatus() != 200) {
 | 
				
			||||||
            common_log(LOG_ERR, "Salmon at $endpoint_uri returned status " .
 | 
					                common_log(LOG_ERR, "Salmon ($class) at $endpoint_uri returned status " .
 | 
				
			||||||
                    $response->getStatus() . ': ' . $response->getBody());
 | 
					                    $response->getStatus() . ': ' . $response->getBody());
 | 
				
			||||||
            return false;
 | 
					                continue;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function createMagicEnv($text, $actor)
 | 
					            // Success!
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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);
 | 
					        $user = User::staticGet('id', $actor->id);
 | 
				
			||||||
        if ($user->id) {
 | 
					        if ($user->id) {
 | 
				
			||||||
@@ -101,12 +134,32 @@ class Salmon
 | 
				
			|||||||
        return $magic_env->toXML($env);
 | 
					        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)
 | 
					    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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										60
									
								
								plugins/OStatus/tests/MagicEnvelopeTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								plugins/OStatus/tests/MagicEnvelopeTest.php
									
									
									
									
									
										Normal 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'");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -72,6 +72,8 @@ class OStatusTester extends TestBase
 | 
				
			|||||||
        $base = 'test' . mt_rand(1, 1000000);
 | 
					        $base = 'test' . mt_rand(1, 1000000);
 | 
				
			||||||
        $this->pub = new SNTestClient($this->a, 'pub' . $base, 'pw-' . mt_rand(1, 1000000), $timeout);
 | 
					        $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->sub = new SNTestClient($this->b, 'sub' . $base, 'pw-' . mt_rand(1, 1000000), $timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->group = 'group' . $base;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function run()
 | 
					    function run()
 | 
				
			||||||
@@ -163,6 +165,39 @@ class OStatusTester extends TestBase
 | 
				
			|||||||
        $this->assertFalse($this->pub->hasSubscriber($this->sub->getProfileUri()));
 | 
					        $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
 | 
					class SNTestClient extends TestBase
 | 
				
			||||||
@@ -534,6 +569,63 @@ class SNTestClient extends TestBase
 | 
				
			|||||||
        return false;
 | 
					        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?
 | 
					// @fixme switch to commandline.inc?
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										92
									
								
								plugins/OStatus/tests/slap.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								plugins/OStatus/tests/slap.php
									
									
									
									
									
										Normal 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";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -382,6 +382,29 @@ class ActivityParseTests extends PHPUnit_Framework_TestCase
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testExample10()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        global $_example10;
 | 
				
			||||||
 | 
					        $dom = new DOMDocument();
 | 
				
			||||||
 | 
					        $dom->loadXML($_example10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // example 10 is a PuSH item of a post on a group feed, as generated
 | 
				
			||||||
 | 
					        // by 0.9.7 code after migration away from <activity:actor> to <author>
 | 
				
			||||||
 | 
					        $feed = $dom->documentElement;
 | 
				
			||||||
 | 
					        $entry = $dom->getElementsByTagName('entry')->item(0);
 | 
				
			||||||
 | 
					        $expected = 'http://lazarus.local/mublog/user/557';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Reading just the entry alone should pick up its own <author>
 | 
				
			||||||
 | 
					        // as the actor.
 | 
				
			||||||
 | 
					        $act = new Activity($entry);
 | 
				
			||||||
 | 
					        $this->assertEquals($act->actor->id, $expected);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Reading the entry in feed context used to be buggy, picking up
 | 
				
			||||||
 | 
					        // the feed's <activity:subject> which referred to the group.
 | 
				
			||||||
 | 
					        // It should now be returning the expected author entry...
 | 
				
			||||||
 | 
					        $act = new Activity($entry, $feed);
 | 
				
			||||||
 | 
					        $this->assertEquals($act->actor->id, $expected);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$_example1 = <<<EXAMPLE1
 | 
					$_example1 = <<<EXAMPLE1
 | 
				
			||||||
@@ -792,3 +815,93 @@ $_example9 = <<<EXAMPLE9
 | 
				
			|||||||
    </entry>
 | 
					    </entry>
 | 
				
			||||||
</feed>
 | 
					</feed>
 | 
				
			||||||
EXAMPLE9;
 | 
					EXAMPLE9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sample PuSH entry from a group feed in 0.9.7
 | 
				
			||||||
 | 
					// Old <activity:actor> has been removed from entries in this version.
 | 
				
			||||||
 | 
					// A bug in the order of input processing meant that we were incorrectly
 | 
				
			||||||
 | 
					// reading the feed's <activity:subject> instead of the entry's <author>,
 | 
				
			||||||
 | 
					// causing the entry to get rejected as malformed (groups can't post on
 | 
				
			||||||
 | 
					// their own; we want to see the actual author's info here).
 | 
				
			||||||
 | 
					$_example10 = <<<EXAMPLE10
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/">
 | 
				
			||||||
 | 
					 <generator uri="http://status.net" version="0.9.7alpha1">StatusNet</generator>
 | 
				
			||||||
 | 
					 <id>http://lazarus.local/mublog/api/statusnet/groups/timeline/22.atom</id>
 | 
				
			||||||
 | 
					 <title>grouptest316173 timeline</title>
 | 
				
			||||||
 | 
					 <subtitle>Updates from grouptest316173 on Blaguette!</subtitle>
 | 
				
			||||||
 | 
					 <logo>http://lazarus.local/mublog/theme/default/default-avatar-profile.png</logo>
 | 
				
			||||||
 | 
					 <updated>2011-01-06T22:44:18+00:00</updated>
 | 
				
			||||||
 | 
					<author>
 | 
				
			||||||
 | 
					 <activity:object-type>http://activitystrea.ms/schema/1.0/group</activity:object-type>
 | 
				
			||||||
 | 
					 <uri>http://lazarus.local/mublog/group/22/id</uri>
 | 
				
			||||||
 | 
					 <name>grouptest316173</name>
 | 
				
			||||||
 | 
					 <link rel="alternate" type="text/html" href="http://lazarus.local/mublog/group/22/id"/>
 | 
				
			||||||
 | 
					 <link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://lazarus.local/mublog/theme/default/default-avatar-profile.png"/>
 | 
				
			||||||
 | 
					 <link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://lazarus.local/mublog/theme/default/default-avatar-stream.png"/>
 | 
				
			||||||
 | 
					 <link rel="avatar" type="image/png" media:width="24" media:height="24" href="http://lazarus.local/mublog/theme/default/default-avatar-mini.png"/>
 | 
				
			||||||
 | 
					 <poco:preferredUsername>grouptest316173</poco:preferredUsername>
 | 
				
			||||||
 | 
					 <poco:displayName>grouptest316173</poco:displayName>
 | 
				
			||||||
 | 
					</author>
 | 
				
			||||||
 | 
					<activity:subject>
 | 
				
			||||||
 | 
					 <activity:object-type>http://activitystrea.ms/schema/1.0/group</activity:object-type>
 | 
				
			||||||
 | 
					 <id>http://lazarus.local/mublog/group/22/id</id>
 | 
				
			||||||
 | 
					 <title>grouptest316173</title>
 | 
				
			||||||
 | 
					 <link rel="alternate" type="text/html" href="http://lazarus.local/mublog/group/22/id"/>
 | 
				
			||||||
 | 
					 <link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://lazarus.local/mublog/theme/default/default-avatar-profile.png"/>
 | 
				
			||||||
 | 
					 <link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://lazarus.local/mublog/theme/default/default-avatar-stream.png"/>
 | 
				
			||||||
 | 
					 <link rel="avatar" type="image/png" media:width="24" media:height="24" href="http://lazarus.local/mublog/theme/default/default-avatar-mini.png"/>
 | 
				
			||||||
 | 
					 <poco:preferredUsername>grouptest316173</poco:preferredUsername>
 | 
				
			||||||
 | 
					 <poco:displayName>grouptest316173</poco:displayName>
 | 
				
			||||||
 | 
					</activity:subject>
 | 
				
			||||||
 | 
					 <link href="http://lazarus.local/mublog/group/grouptest316173" rel="alternate" type="text/html"/>
 | 
				
			||||||
 | 
					 <link href="http://lazarus.local/mublog/main/push/hub" rel="hub"/>
 | 
				
			||||||
 | 
					 <link href="http://lazarus.local/mublog/main/salmon/group/22" rel="salmon"/>
 | 
				
			||||||
 | 
					 <link href="http://lazarus.local/mublog/main/salmon/group/22" rel="http://salmon-protocol.org/ns/salmon-replies"/>
 | 
				
			||||||
 | 
					 <link href="http://lazarus.local/mublog/main/salmon/group/22" rel="http://salmon-protocol.org/ns/salmon-mention"/>
 | 
				
			||||||
 | 
					 <link href="http://lazarus.local/mublog/api/statusnet/groups/timeline/22.atom" rel="self" type="application/atom+xml"/>
 | 
				
			||||||
 | 
					 <statusnet:group_info member_count="2"></statusnet:group_info>
 | 
				
			||||||
 | 
					<entry>
 | 
				
			||||||
 | 
					 <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
 | 
				
			||||||
 | 
					 <id>http://lazarus.local/mublog/notice/1243</id>
 | 
				
			||||||
 | 
					 <title>Group post from local to !grouptest316173, should go out over push.</title>
 | 
				
			||||||
 | 
					 <content type="html">Group post from local to !<span class="vcard"><a href="http://lazarus.local/mublog/group/22/id" class="url"><span class="fn nickname">grouptest316173</span></a></span>, should go out over push.</content>
 | 
				
			||||||
 | 
					 <link rel="alternate" type="text/html" href="http://lazarus.local/mublog/notice/1243"/>
 | 
				
			||||||
 | 
					 <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
 | 
				
			||||||
 | 
					 <published>2011-01-06T22:44:18+00:00</published>
 | 
				
			||||||
 | 
					 <updated>2011-01-06T22:44:18+00:00</updated>
 | 
				
			||||||
 | 
					 <author>
 | 
				
			||||||
 | 
					  <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
 | 
				
			||||||
 | 
					  <uri>http://lazarus.local/mublog/user/557</uri>
 | 
				
			||||||
 | 
					  <name>Pubtest316173 Smith</name>
 | 
				
			||||||
 | 
					  <link rel="alternate" type="text/html" href="http://lazarus.local/mublog/pubtest316173"/>
 | 
				
			||||||
 | 
					  <link rel="avatar" type="image/png" media:width="96" media:height="96" href="http://lazarus.local/mublog/theme/default/default-avatar-profile.png"/>
 | 
				
			||||||
 | 
					  <link rel="avatar" type="image/png" media:width="48" media:height="48" href="http://lazarus.local/mublog/theme/default/default-avatar-stream.png"/>
 | 
				
			||||||
 | 
					  <link rel="avatar" type="image/png" media:width="24" media:height="24" href="http://lazarus.local/mublog/theme/default/default-avatar-mini.png"/>
 | 
				
			||||||
 | 
					  <poco:preferredUsername>pubtest316173</poco:preferredUsername>
 | 
				
			||||||
 | 
					  <poco:displayName>Pubtest316173 Smith</poco:displayName>
 | 
				
			||||||
 | 
					  <poco:note>Stub account for OStatus tests.</poco:note>
 | 
				
			||||||
 | 
					  <poco:urls>
 | 
				
			||||||
 | 
					   <poco:type>homepage</poco:type>
 | 
				
			||||||
 | 
					   <poco:value>http://example.org/pubtest316173</poco:value>
 | 
				
			||||||
 | 
					   <poco:primary>true</poco:primary>
 | 
				
			||||||
 | 
					  </poco:urls>
 | 
				
			||||||
 | 
					 </author>
 | 
				
			||||||
 | 
					 <link rel="ostatus:conversation" href="http://lazarus.local/mublog/conversation/1131"/>
 | 
				
			||||||
 | 
					 <link rel="ostatus:attention" href="http://lazarus.local/mublog/group/22/id"/>
 | 
				
			||||||
 | 
					 <link rel="mentioned" href="http://lazarus.local/mublog/group/22/id"/>
 | 
				
			||||||
 | 
					 <category term="grouptest316173"></category>
 | 
				
			||||||
 | 
					 <source>
 | 
				
			||||||
 | 
					  <id>http://lazarus.local/mublog/api/statuses/user_timeline/557.atom</id>
 | 
				
			||||||
 | 
					  <title>Pubtest316173 Smith</title>
 | 
				
			||||||
 | 
					  <link rel="alternate" type="text/html" href="http://lazarus.local/mublog/pubtest316173"/>
 | 
				
			||||||
 | 
					  <link rel="self" type="application/atom+xml" href="http://lazarus.local/mublog/api/statuses/user_timeline/557.atom"/>
 | 
				
			||||||
 | 
					  <link rel="license" href="http://creativecommons.org/licenses/by/3.0/"/>
 | 
				
			||||||
 | 
					  <icon>http://lazarus.local/mublog/theme/default/default-avatar-profile.png</icon>
 | 
				
			||||||
 | 
					  <updated>2011-01-06T22:44:18+00:00</updated>
 | 
				
			||||||
 | 
					 </source>
 | 
				
			||||||
 | 
					 <link rel="self" type="application/atom+xml" href="http://lazarus.local/mublog/api/statuses/show/1243.atom"/>
 | 
				
			||||||
 | 
					 <link rel="edit" type="application/atom+xml" href="http://lazarus.local/mublog/api/statuses/show/1243.atom"/>
 | 
				
			||||||
 | 
					 <statusnet:notice_info local_id="1243" source="api"></statusnet:notice_info>
 | 
				
			||||||
 | 
					</entry>
 | 
				
			||||||
 | 
					</feed>
 | 
				
			||||||
 | 
					EXAMPLE10;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user