code:
<?php

/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Database schema utilities
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Database
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL')) { exit(1); }

class SchemaUpdater
{
    public function __construct($schema)
    {
        $this->schema = $schema;
        $this->checksums = $this->getChecksums();
    }

    /**
     * @param string $tableName
     * @param array $tableDef
     */
    public function register($tableName, array $tableDef)
    {
        // Check if the table we're registering is related to a Managed_DataObject
        if (is_a(ucfirst($tableName), 'Managed_DataObject', true)) {
            call_user_func("{$tableName}::beforeSchemaUpdate");
        }

        $this->tables[$tableName] = $tableDef;
    }

    /**
     * Go ping em!
     *
     * @fixme handle tables that belong on different database servers...?
     */
    public function checkSchema()
    {
        $checksums = $this->checksums;
        foreach ($this->tables as $table => $def) {
            $checksum = $this->checksum($def);
            if (empty($checksums[$table])) {
                common_log(LOG_DEBUG, "No previous schema_version for $table: updating to $checksum");
            } else if ($checksums[$table] == $checksum) {
                common_log(LOG_DEBUG, "Last schema_version for $table up to date: $checksum");
                continue;
            } else {
                common_log(LOG_DEBUG, "Last schema_version for $table is {$checksums[$table]}: updating to $checksum");
            }
            //$this->conn->query('BEGIN');
            $this->schema->ensureTable($table, $def);
            $this->saveChecksum($table, $checksum);
            //$this->conn->commit();
        }
    }

    /**
     * Calculate a checksum for this table definition array.
     *
     * @param array $def
     * @return string
     */
    public function checksum(array $def)
    {
        $flat = serialize($def);
        return sha1($flat);
    }

    /**
     * Pull all known table checksums into an array for easy lookup.
     *
     * @return array: associative array of table names to checksum strings
     */
    protected function getChecksums()
    {
        $checksums = array();

        try {
            $sv = new Schema_version();
            $sv->find();
            while ($sv->fetch()) {
                $checksums[$sv->table_name] = $sv->checksum;
            }

            return $checksums;
        } catch (Exception $e) {
            // no dice!
            common_log(LOG_DEBUG, "Possibly schema_version table doesn't exist yet.");
        }

        return $checksums;
    }

    /**
     * Save or update current available checksums.
     *
     * @param string $table
     * @param string $checksum
     */
    protected function saveChecksum($table, $checksum)
    {
        try {
            $sv = new Schema_version();
            $sv->table_name = $table;
            $sv->checksum = $checksum;
            $sv->modified = common_sql_now();
            if (isset($this->checksums[$table])) {
                $sv->update();
            } else {
                $sv->insert();
            }
        } catch (Exception $e) {
            // no dice!
            common_log(LOG_DEBUG, "Possibly schema_version table doesn't exist yet.");
        }
        $this->checksums[$table] = $checksum;
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Filtering notice stream that recognizes notice scope
 *
 * 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/>.
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * Class comment
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class ScopingNoticeStream extends FilteringNoticeStream
{
    protected $profile;

    function __construct($upstream, $profile = -1)
    {
        parent::__construct($upstream);

        // Invalid but not null
        if (is_int($profile) && $profile == -1) {
            $profile = Profile::current();
        }

        $this->profile = $profile;
    }

    /**
     * Only return notices where the profile is in scope
     *
     * @param Notice $notice The notice to check
     *
     * @return boolean whether to include the notice
     */

    function filter($notice)
    {
        return $notice->inScope($this->profile);
    }

    function prefill($notices)
    {
        // XXX: this should probably only be in the scoping one.

        Notice::fillGroups($notices);
        Notice::fillReplies($notices);

        if (common_config('notice', 'hidespam')) {

            $profiles = Notice::getProfiles($notices);

            foreach ($profiles as $profile) {
                $pids[] = $profile->id;
            }

            Profile_role::pivotGet('profile_id',
                                   $pids,
                                   array('role' => Profile_role::SILENCED));
        }
    }
}

repo_name: piratas/tortuga

license: mit


code:
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, 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/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }

class SearchEngine
{
    protected $target;
    protected $table;

    function __construct($target, $table)
    {
        $this->target = $target;
        $this->table = $table;
    }

    function query($q)
    {
    }

    function limit($offset, $count, $rss = false)
    {
        return $this->target->limit($offset, $count);
    }

    function set_sort_mode($mode)
    {
        switch ($mode) {
        case 'chron':
            return $this->target->orderBy('created DESC');
            break;
        case 'reverse_chron':
            return $this->target->orderBy('created ASC');
            break;
        case 'nickname_desc':
            if ($this->table != 'profile') {
                throw new Exception(
                    'nickname_desc sort mode can only be use when searching profile.'
                );
            } else {
                return $this->target->orderBy('nickname DESC');
            }
            break;
        case 'nickname_asc':
            if ($this->table != 'profile') {
                throw new Exception(
                    'nickname_desc sort mode can only be use when searching profile.'
                );
            } else {
                return $this->target->orderBy('nickname ASC');
            }
            break;
        default:
            return $this->target->orderBy('created DESC');
            break;
        }
    }
}

class MySQLSearch extends SearchEngine
{
    function query($q)
    {
        if ('profile' === $this->table) {
            $this->target->whereAdd('MATCH(nickname, fullname, location, bio, homepage) ' .
                                    'AGAINST (\''.$this->target->escape($q).'\' IN BOOLEAN MODE)');
            if (strtolower($q) != $q) {
                $this->target->whereAdd('MATCH(nickname, fullname, location, bio, homepage) ' .
                                        'AGAINST (\''.$this->target->escape(strtolower($q)).'\' IN BOOLEAN MODE)', 'OR');
            }
            return true;
        } else if ('notice' === $this->table) {

            // Don't show imported notices
            $this->target->whereAdd('notice.is_local != ' . Notice::GATEWAY);

            if (strtolower($q) != $q) {
                $this->target->whereAdd("( MATCH(content) AGAINST ('" . $this->target->escape($q) .
                    "' IN BOOLEAN MODE)) OR ( MATCH(content) " .
                    "AGAINST ('"  . $this->target->escape(strtolower($q)) .
                    "' IN BOOLEAN MODE))");
            } else {
                $this->target->whereAdd('MATCH(content) ' .
                                         'AGAINST (\''.$this->target->escape($q).'\' IN BOOLEAN MODE)');
            }

            return true;
        } else {
            throw new ServerException('Unknown table: ' . $this->table);
        }
    }
}

class MySQLLikeSearch extends SearchEngine
{
    function query($q)
    {
        if ('profile' === $this->table) {
            $qry = sprintf('(nickname LIKE "%%%1$s%%" OR '.
                           ' fullname LIKE "%%%1$s%%" OR '.
                           ' location LIKE "%%%1$s%%" OR '.
                           ' bio      LIKE "%%%1$s%%" OR '.
                           ' homepage LIKE "%%%1$s%%")', $this->target->escape($q, true));
        } else if ('notice' === $this->table) {
            $qry = sprintf('content LIKE "%%%1$s%%"', $this->target->escape($q, true));
        } else {
            throw new ServerException('Unknown table: ' . $this->table);
        }

        $this->target->whereAdd($qry);

        return true;
    }
}

class PGSearch extends SearchEngine
{
    function query($q)
    {
        if ('profile' === $this->table) {
            return $this->target->whereAdd('textsearch @@ plainto_tsquery(\''.$this->target->escape($q).'\')');
        } else if ('notice' === $this->table) {

            // XXX: We need to filter out gateway notices (notice.is_local = -2) --Zach

            return $this->target->whereAdd('to_tsvector(\'english\', content) @@ plainto_tsquery(\''.$this->target->escape($q).'\')');
        } else {
            throw new ServerException('Unknown table: ' . $this->table);
        }
    }
}


code:
<?php
/**
 * Base search action class.
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Robin Millette <millette@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, 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/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/searchgroupnav.php';

/**
 * Base search action class.
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Robin Millette <millette@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class SearchAction extends Action
{
    /**
     * Return true if read only.
     *
     * @return boolean true
     */
    function isReadOnly($args)
    {
        return true;
    }

    function handle($args)
    {
        parent::handle($args);
        $this->showPage();
    }

    function showTop($arr=null)
    {
        $error = null;
        if ($arr) {
            $error = $arr[1];
        }
        if (!empty($error)) {
            $this->element('p', 'error', $error);
        } else {
            $instr = $this->getInstructions();
            $output = common_markup_to_html($instr);
            $this->elementStart('div', 'instructions');
            $this->raw($output);
            $this->elementEnd('div');
        }
    }

    function title()
    {
        return null;
    }

    function showContent() {
        $this->showTop();
        $this->showForm();
    }

    function showForm($error=null)
    {
        $q = $this->trimmed('q');
        $page = $this->trimmed('page', 1);
        $this->elementStart('form', array('method' => 'get',
                                           'id' => 'form_search',
                                           'class' => 'form_settings',
                                           'action' => common_local_url($this->trimmed('action'))));
        $this->elementStart('fieldset');
        // TRANS: Fieldset legend for the search form.
        $this->element('legend', null, _('Search site'));
        $this->elementStart('ul', 'form_data');
        $this->elementStart('li');
        if (!common_config('site', 'fancy')) {
            $this->hidden('action', $this->trimmed('action'));
        }
        // TRANS: Used as a field label for the field where one or more keywords
        // TRANS: for searching can be entered.
        $this->input('q', _('Keyword(s)'), $q);
        // TRANS: Button text for searching site.
        $this->element('input', array('type'=>'submit', 'class'=>'submit', 'value'=>_m('BUTTON','Search')));
        $this->elementEnd('li');
        $this->elementEnd('ul');
        $this->elementEnd('fieldset');
        $this->elementEnd('form');
        if ($q) {
            $this->showResults($q, $page);
        }
    }

    function searchSuggestions($q) {
        // Don't change these long strings to HEREDOC; xgettext won't pick them up.
        // TRANS: Standard search suggestions shown when a search does not give any results.
        $message = _("* Make sure all words are spelled correctly.
* Try different keywords.
* Try more general keywords.
* Try fewer keywords.");
            $message .= "\n";

        if (!common_config('site', 'private')) {
            $qe = urlencode($q);
            $message .= "\n";
            // Don't change these long strings to HEREDOC; xgettext won't pick them up.
            // TRANS: Standard search suggestions shown when a search does not give any results.
            $message .= sprintf(_("You can also try your search on other engines:

* [DuckDuckGo](https://duckduckgo.com/?q=site%%3A%%%%site.server%%%%+%s)
* [Ixquick](https://ixquick.com/do/search?query=site%%3A%%%%site.server%%%%+%s)
* [Google](https://www.google.com/search?q=site%%3A%%%%site.server%%%%+%s)
* [Yahoo!](https://search.yahoo.com/search?p=site%%3A%%%%site.server%%%%+%s)
"), $qe, $qe, $qe, $qe);
            $message .= "\n";
        }
        $this->elementStart('div', 'help instructions');
        $this->raw(common_markup_to_html($message));
        $this->elementEnd('div');
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Form for searching a StatusNet site
 *
 * 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/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * Form for searching a StatusNet site
 *
 * @category  General
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class SearchForm extends Form
{
    /**
     * Visible or invisible data elements
     *
     * Display the form fields that make up the data of the form.
     * Sub-classes should overload this to show their data.
     *
     * @return void
     */
    function formData()
    {
        $this->out->element('input', array('name' => 'q',
                                           'size' => 20,
                                           'id' => 'search-q'));
    }

    /**
     * Buttons for form actions
     *
     * Submit and cancel buttons (or whatever)
     * Sub-classes should overload this to show their own buttons.
     *
     * @return void
     */
    function formActions()
    {
        $this->out->element('input', array('type' => 'submit',
                                           // TRANS: Button text for search button on search form.
                                           'value' => _m('BUTTON','Search')));
    }

    /**
     * ID of the form
     *
     * Should be unique on the page. Sub-classes should overload this
     * to show their own IDs.
     *
     * @return int ID of the form
     */
    function id()
    {
        return 'header-search';
    }

    /**
     * Action of the form.
     *
     * URL to post to. Should be overloaded by subclasses to give
     * somewhere to post to.
     *
     * @return string URL to post to
     */
    function action()
    {
        return common_local_url('noticesearch');
    }

    function method()
    {
        return 'get';
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Menu for search group of actions
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Menu
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Menu for public group of actions
 *
 * @category Menu
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      Widget
 */
class SearchGroupNav extends Menu
{
    var $q = null;

    /**
     * Construction
     *
     * @param Action $action current action, used for output
     */
    function __construct(Action $action=null, $q = null)
    {
        parent::__construct($action);
        $this->q = $q;
    }

    /**
     * Show the menu
     *
     * @return void
     */
    function show()
    {
        $action_name = $this->action->trimmed('action');
        $this->action->elementStart('ul', array('class' => 'nav'));
        $args = array();
        if ($this->q) {
            $args['q'] = $this->q;
        }
        // TRANS: Menu item in search group navigation panel.
        $this->out->menuItem(common_local_url('peoplesearch', $args), _m('MENU','People'),
            // TRANS: Menu item title in search group navigation panel.
            _('Find people on this site'), $action_name == 'peoplesearch', 'nav_search_people');
        // TRANS: Menu item in search group navigation panel.
        $this->out->menuItem(common_local_url('noticesearch', $args), _m('MENU','Notices'),
            // TRANS: Menu item title in search group navigation panel.
            _('Find content of notices'), $action_name == 'noticesearch', 'nav_search_notice');
        // TRANS: Menu item in search group navigation panel.
        $this->out->menuItem(common_local_url('groupsearch', $args), _m('MENU','Groups'),
            // TRANS: Menu item title in search group navigation panel.
            _('Find groups on this site'), $action_name == 'groupsearch', 'nav_search_group');
        $this->action->elementEnd('ul');
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Stream of notice results
 *
 * 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/>.
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * Stream of notice search results
 *
 * @category  General
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class SearchNoticeStream extends ScopingNoticeStream
{
    function __construct($q, $profile = -1)
    {
        if (is_int($profile) && $profile == -1) {
            $profile = Profile::current();
        }

        parent::__construct(new RawSearchNoticeStream($q), $profile);
    }
}

class RawSearchNoticeStream extends NoticeStream
{
    protected $q;

    function __construct($q)
    {
        $this->q = $q;
    }

    function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        $notice = new Notice();

        $search_engine = $notice->getSearchEngine('notice');
        $search_engine->set_sort_mode('chron');

        $search_engine->limit($offset, $limit);

        $ids = array();

        // wtf?

        $search_engine->query($this->q);

        if ($notice->find()) {
            while ($notice->fetch()) {
                $ids[] = $notice->id;
            }
        }

        return $ids;
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Secondary menu, shown at foot of all pages
 *
 * 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/>.
 *
 * @category  Cache
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * Secondary menu, shown at the bottom of all pages
 *
 * @category  General
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class SecondaryNav extends Menu
{
    function show()
    {
        $this->out->elementStart('ul', array('class' => 'nav',
                                             'id' => 'site_nav_global_secondary'));
        if (Event::handle('StartSecondaryNav', array($this->action))) {
            $this->out->menuItem(common_local_url('doc', array('title' => 'help')),
                                 // TRANS: Secondary navigation menu item leading to help on StatusNet.
                                 _m('MENU','Help'));
            $this->out->menuItem(common_local_url('doc', array('title' => 'about')),
                                 // TRANS: Secondary navigation menu item leading to text about StatusNet site.
                                 _m('MENU','About'));
            $this->out->menuItem(common_local_url('doc', array('title' => 'faq')),
                                 // TRANS: Secondary navigation menu item leading to Frequently Asked Questions.
                                 _m('MENU','FAQ'));
            $bb = common_config('site', 'broughtby');
            if (!empty($bb)) {
                $this->out->menuItem(common_local_url('doc', array('title' => 'tos')),
                                     // TRANS: Secondary navigation menu item leading to Terms of Service.
                                     _m('MENU','TOS'));
            }
            $this->out->menuItem(common_local_url('doc', array('title' => 'privacy')),
                                 // TRANS: Secondary navigation menu item leading to privacy policy.
                                 _m('MENU','Privacy'));
            $this->out->menuItem(common_local_url('doc', array('title' => 'source')),
                                 // TRANS: Secondary navigation menu item. Leads to information about StatusNet and its license.
                                 _m('MENU','Source'));
            $this->out->menuItem(common_local_url('version'),
                                 // TRANS: Secondary navigation menu item leading to version information on the StatusNet site.
                                 _m('MENU','Version'));
            $this->out->menuItem(common_local_url('doc', array('title' => 'contact')),
                                 // TRANS: Secondary navigation menu item leading to e-mail contact information on the
                                 // TRANS: StatusNet site, where to report bugs, ...
                                 _m('MENU','Contact'));
            Event::handle('EndSecondaryNav', array($this->action));
        }
        $this->out->elementEnd('ul');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Base class for sections (sidebar widgets)
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }

/**
 * Base class for sections
 *
 * These are the widgets that show interesting data about a person
 * group, or site.
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
abstract class Section extends Widget
{
    /**
     * Show the form
     *
     * Uses a recipe to output the form.
     *
     * @return void
     * @see Widget::show()
     */
    function show()
    {
        $this->out->elementStart('div',
                                 array('id' => $this->divId(),
                                       'class' => 'section'));

        $this->showTitle();

        $have_more = $this->showContent();

        if ($have_more) {
            $this->showMore();
        }

        $this->out->elementEnd('div');
    }

    function showTitle()
    {
        $link = $this->link();
        if (!empty($link)) {
            $this->out->elementStart('h2');
            $this->out->element('a', array('href' => $link), $this->title());
            $this->out->elementEnd('h2');
        } else {
            $this->out->element('h2', null,
                                $this->title());
        }
    }

    function showMore()
    {
        $this->out->elementStart('p');
        $this->out->element('a', array('href' => $this->moreUrl(),
                                       'class' => 'more'),
                            $this->moreTitle());
        $this->out->elementEnd('p');
    }

    abstract public function divId();

    function title()
    {
        // TRANS: Default title for section/sidebar widget.
        return _('Untitled section');
    }

    function link()
    {
        return null;
    }

    function showContent()
    {
        $this->out->element('p', null,
                            // TRANS: Default content for section/sidebar widget.
                            _('(None)'));
        return false;
    }

    function moreUrl()
    {
        return null;
    }

    function moreTitle()
    {
        // TRANS: Default "More..." title for section/sidebar widget.
        return _('More...');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * widget for displaying a list of notices
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  UI
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2008 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }

/**
 * widget for displaying a list of notices in the primary content area
 *
 * @category UI
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 * @see      Notice
 * @see      NoticeListItem
 * @see      ProfileNoticeList
 */
class SectionNoticeList extends NoticeList
{
    protected $addressees = false;
    protected $attachments = false;
    protected $maxchars = 140;
    protected $options = false;
    protected $show_n = NOTICES_PER_SECTION;
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Tags for a profile
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Action
 * @package   StatusNet
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL')) { exit(1); }

class SelftagsWidget extends PeopletagsWidget
{
    function url($tag)
    {
        // link to self tag page
        return common_local_url('selftag', array('tag' => $tag));
    }

    function label()
    {
        // TRANS: Label in self tags widget.
        return _m('LABEL','Tags');
    }
}


code:
<?php

/**
 * Server error action.
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, 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/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/error.php';

/**
 * Class for displaying HTTP server errors
 *
 * Note: The older util.php class simply printed a string, but the spec
 * says that 500 errors should be treated similarly to 400 errors, and
 * it's easier to give an HTML response.  Maybe we can customize these
 * to display some funny animal cartoons.  If not, we can probably role
 * these classes up into a single class.
 *
 * See: http://tools.ietf.org/html/rfc2616#section-10
 *
 * @category Action
 * @package  StatusNet
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */

class ServerErrorAction extends ErrorAction
{
    static $status = array(500 => 'Internal Server Error',
                           501 => 'Not Implemented',
                           502 => 'Bad Gateway',
                           503 => 'Service Unavailable',
                           504 => 'Gateway Timeout',
                           505 => 'HTTP Version Not Supported');

    function __construct($message='Error', $code=500, $ex=null)
    {
        parent::__construct($message, $code);

        $this->default = 500;

        // Server errors must be logged.
        $log = "ServerErrorAction: $code $message";
        if ($ex) {
            $log .= "\n" . $ex->getTraceAsString();
        }
        common_log(LOG_ERR, $log);
    }

    // XXX: Should these error actions even be invokable via URI?

    function handle($args)
    {
        parent::handle($args);

        $this->code = $this->trimmed('code');

        if (!$this->code || $code < 500 || $code > 599) {
            $this->code = $this->default;
        }

        $this->message = $this->trimmed('message');

        if (!$this->message) {
            $this->message = "Server Error $this->code";
        }

        $this->showPage();
    }

    /**
     *  To specify additional HTTP headers for the action
     *
     *  @return void
     */
    function extraHeaders()
    {
        $status_string = @self::$status[$this->code];
        header('HTTP/1.1 '.$this->code.' '.$status_string);
    }

    /**
     * Page title.
     *
     * @return page title
     */

    function title()
    {
        return @self::$status[$this->code];
    }

}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * class for a server exception (user error)
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Exception
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Class for server exceptions
 *
 * Subclass of PHP Exception for server errors. The user typically can't fix these.
 *
 * @category Exception
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */

class ServerException extends Exception
{
    public function __construct($message = null, $code = 400) {
        parent::__construct($message, $code);
    }

    public function __toString() {
        return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Base class for settings actions
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Settings
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Base class for settings group of actions
 *
 * @category Settings
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      Widget
 */

class SettingsAction extends Action
{
    /**
     * A message for the user.
     */

    var $msg = null;

    /**
     * Whether the message is a good one or a bad one.
     */

    var $success = false;

    /**
     * Handle input and output a page
     *
     * @param array $args $_REQUEST arguments
     *
     * @return void
     */

    function handle($args)
    {
        parent::handle($args);
        if (!common_logged_in()) {
            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
            $this->clientError(_('Not logged in.'));
        } else if (!common_is_real_login()) {
            // Cookie theft means that automatic logins can't
            // change important settings or see private info, and
            // _all_ our settings are important
            common_set_returnto($this->selfUrl());
            $user = common_current_user();
            if (Event::handle('RedirectToLogin', array($this, $user))) {
                common_redirect(common_local_url('login'), 303);
            }
        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->handlePost();
        } else {
            $this->showForm();
        }
    }

    /**
     * Handle a POST request
     *
     * @return boolean success flag
     */

    function handlePost()
    {
        return false;
    }

    /**
     * show the settings form
     *
     * @param string $msg     an extra message for the user
     * @param string $success good message or bad message?
     *
     * @return void
     */

    function showForm($msg=null, $success=false)
    {
        $this->msg     = $msg;
        $this->success = $success;

        $this->showPage();
    }

    /**
     * show human-readable instructions for the page
     *
     * @return void
     */

    function showPageNotice()
    {
        if ($this->msg) {
            $this->element('div', ($this->success) ? 'success' : 'error',
                           $this->msg);
        } else {
            $inst   = $this->getInstructions();
            $output = common_markup_to_html($inst);

            $this->elementStart('div', 'instructions');
            $this->raw($output);
            $this->elementEnd('div');
        }
    }

    /**
     * instructions recipe for sub-classes
     *
     * Subclasses should override this to return readable instructions. They'll
     * be processed by common_markup_to_html().
     *
     * @return string instructions text
     */

    function getInstructions()
    {
        return '';
    }

    /**
     * Show the local navigation menu
     *
     * This is the same for all settings, so we show it here.
     *
     * @return void
     */

    function showLocalNav()
    {
        $menu = new SettingsNav($this);
        $menu->show();
    }

    /**
     * Show notice form.
     *
     * @return nothing
     */

    function showNoticeForm()
    {
        return;
    }

    function showProfileBlock()
    {
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010,2011, StatusNet, Inc.
 *
 * Settings menu
 *
 * 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/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2010,2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * A widget for showing the settings group local nav menu
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      HTMLOutputter
 */
class SettingsNav extends Menu
{
    /**
     * Show the menu
     *
     * @return void
     */
    function show()
    {
        $actionName = $this->action->trimmed('action');
        $user = common_current_user();
        $nickname = $user->nickname;
        $name = $user->getProfile()->getBestName();

        $stub = new HomeStubNav($this->action);
        $this->submenu(_m('MENU','Home'), $stub);

        $this->action->elementStart('ul');
        $this->action->elementStart('li');
        // TRANS: Header in settings navigation panel.
        $this->action->element('h3', null, _m('HEADER','Settings'));
        $this->action->elementStart('ul', array('class' => 'nav'));

        if (Event::handle('StartAccountSettingsNav', array(&$this->action))) {
            $this->action->menuItem(common_local_url('profilesettings'),
                                    // TRANS: Menu item in settings navigation panel.
                                    _m('MENU','Profile'),
                                    // TRANS: Menu item title in settings navigation panel.
                                    _('Change your profile settings'),
                                    $actionName == 'profilesettings');

            $this->action->menuItem(common_local_url('avatarsettings'),
                                    // TRANS: Menu item in settings navigation panel.
                                    _m('MENU','Avatar'),
                                    // TRANS: Menu item title in settings navigation panel.
                                    _('Upload an avatar'),
                                    $actionName == 'avatarsettings');

            $this->action->menuItem(common_local_url('passwordsettings'),
                                    // TRANS: Menu item in settings navigation panel.
                                    _m('MENU','Password'),
                                    // TRANS: Menu item title in settings navigation panel.
                                    _('Change your password'),
                                    $actionName == 'passwordsettings');

            $this->action->menuItem(common_local_url('emailsettings'),
                                    // TRANS: Menu item in settings navigation panel.
                                    _m('MENU','Email'),
                                    // TRANS: Menu item title in settings navigation panel.
                                    _('Change email handling'),
                                    $actionName == 'emailsettings');

            $this->action->menuItem(common_local_url('urlsettings'),
                                    // TRANS: Menu item in settings navigation panel.
                                    _m('MENU','URL'),
                                    // TRANS: Menu item title in settings navigation panel.
                                    _('URL shorteners'),
                                    $actionName == 'urlsettings');

            Event::handle('EndAccountSettingsNav', array(&$this->action));

            $haveImPlugin = false;

            Event::handle('HaveImPlugin', array(&$haveImPlugin));

            if ($haveImPlugin) {
                $this->action->menuItem(common_local_url('imsettings'),
                                        // TRANS: Menu item in settings navigation panel.
                                        _m('MENU','IM'),
                                        // TRANS: Menu item title in settings navigation panel.
                                        _('Updates by instant messenger (IM)'),
                                        $actionName == 'imsettings');
            }

            if (common_config('sms', 'enabled')) {
                $this->action->menuItem(common_local_url('smssettings'),
                                        // TRANS: Menu item in settings navigation panel.
                                        _m('MENU','SMS'),
                                        // TRANS: Menu item title in settings navigation panel.
                                        _('Updates by SMS'),
                                        $actionName == 'smssettings');
            }

            $this->action->menuItem(common_local_url('oauthconnectionssettings'),
                                    // TRANS: Menu item in settings navigation panel.
                                    _m('MENU','Connections'),
                                    // TRANS: Menu item title in settings navigation panel.
                                    _('Authorized connected applications'),
                                    $actionName == 'oauthconnectionsettings');

            if (common_config('oldschool', 'enabled')) {
                $this->action->menuItem(common_local_url('oldschoolsettings'),
                                        // TRANS: Menu item in settings navigation panel.
                                        _m('MENU','Old school'),
                                        // TRANS: Menu item title in settings navigation panel.
                                        _('UI tweaks for old-school users'),
                                        $actionName == 'oldschoolsettings');
            }

            Event::handle('EndConnectSettingsNav', array(&$this->action));
        }

        $this->action->elementEnd('ul');
        $this->action->elementEnd('li');
        $this->action->elementEnd('ul');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for silencing a user
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Form
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    exit(1);
}

/**
 * Form for silencing a user
 *
 * @category Form
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      UnSilenceForm
 */
class SilenceForm extends ProfileActionForm
{
    /**
     * Action this form provides
     *
     * @return string Name of the action, lowercased.
     */
    function target()
    {
        return 'silence';
    }

    /**
     * Title of the form
     *
     * @return string Title of the form, internationalized
     */
    function title()
    {
        // TRANS: Title of form to silence a user.
        return _m('TITLE','Silence');
    }

    /**
     * Description of the form
     *
     * @return string description of the form, internationalized
     */
    function description()
    {
        // TRANS: Description of form to silence a user.
        return _('Silence this user');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * A site profile is a set of default settings for a particular style of
 * StatusNet site: public, private, community, etc.
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  Installation
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL')) {
    exit(1);
}

/**
 * Helper class for getting the settings for a particular site profile
 */
class SiteProfile
{
    /**
     * Returns the config settings for a site profile by name
     *
     * @param  string $name name of a site profile
     * @return array  config settings
     */
    static public function getSettings($name)
    {
        $sprofileClass = ucfirst($name) . "Site";

        if (class_exists($sprofileClass)) {
            return call_user_func(array($sprofileClass, 'getSettings'));
        } else {
            common_log(
                LOG_ERR,
                "Unknown site profile '{$name}' specified in config file.",
                __FILE__
            );
            return array();
        }
    }
}

/**
 * Site profile settings contain the list of the default settings (and
 * possibly other information for a particular flavor of StatusNet
 * installation). These will overwrite base defaults in $config global.
 *
 * @category Installation
 * @package  StatusNet
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
abstract class SiteProfileSettings
{
    static function getSettings()
    {
        throw new MethodNotImplementedException(__METHOD__);
    }

    static function corePlugins() {
        return common_config('plugins', 'core');
    }
    static function defaultPlugins() {
        return common_config('plugins', 'default');
    }
}

/**
 * Settings for a 'public' site
 */
class PublicSite extends SiteProfileSettings
{
    /**
     * Get the settings for this site profile
     *
     * @return type array   an array of settings
     */
    static function getSettings() {
        global $config;
        return array(
            // We only want to change these values, not replace entire 'site' array
            'site' => array_merge(
                $config['site'], array(
                    'inviteonly' => false,
                    'private'    => false,
                    'closed'     => false
                )
            ),
            'plugins' => array(
                'core'    => self::corePlugins(),
                'default' => array_merge(self::defaultPlugins(), array(
                    'ExtendedProfile'         => array(),
                    'RegisterThrottle'        => array(),
                ))
            ),
            'discovery' => array('cors' => true) // Allow Cross-Origin Resource Sharing for service discovery (host-meta, XRD, etc.)
        );
    }
}

/**
 * Settings for a 'private' site
 *
 * // XXX Too business oriented?
 */
class PrivateSite extends SiteProfileSettings
{
    /**
     * Get the settings for this site profile
     *
     * @return type array  an array of settings
     */
    static function getSettings() {
        global $config;
        return array(
            // We only want to change these values, not replace entire 'site' array
            'site' => array_merge(
                $config['site'], array(
                    'inviteonly' => true,
                    'private'    => true,
                )
            ),
            'plugins' => array(
                'core'    => self::corePlugins(),
                'default' => array_merge(self::defaultPlugins(), array(
                    'ExtendedProfile'         => array(),
                    'EmailRegistration'       => array(),
                    'MobileProfile'           => array(),
                )),
                'disable-OStatus' => 1,
                'disable-WebFinger' => 1,
             ),
            'public'        => array('localonly' => true),
            'profile'       => array('delete' => 'true'),
            'license'       => array('type'   => 'private'),
            'attachments'   => array(
                // Only allow uploads of pictures and MS Office files
                'supported' => array(
                    'image/png',
                    'image/jpeg',
                    'image/gif',
                    'image/svg+xml',
                    'application/pdf',
                    'application/msword',
                    'application/vnd.ms-office',
                    'application/vnd.ms-excel',
                    'application/vnd.ms-powerpoint',
                    'application/ogg'
                )
             ),
            'discovery' => array('cors'   => false) // Allow Cross-Origin Resource Sharing for service discovery (host-meta, XRD, etc.)
        );
    }
}

/**
 * Settings for a 'community' site
 */
class CommunitySite extends SiteProfileSettings
{
    /**
     * Get the settings for this site profile
     *
     * @return type array  an array of settings
     */
    static function getSettings() {
        global $config;
        return array(
            // We only want to change these values, not replace entire 'site' array
            'site' => array_merge(
                $config['site'], array(
                    'private'    => false,
                    'inviteonly' => true,
                    'closed'     => false
                )
            ),
            'plugins' => array(
                'core'    => self::corePlugins(),
                'default' => array_merge(self::defaultPlugins(), array(
                ))
            ),
            'public'    => array('localonly' => true),
            'discovery' => array('cors' => true) // Allow Cross-Origin Resource Sharing for service discovery (host-meta, XRD, etc.)
        );
    }

}

/**
 * Settings for a 'singleuser' site
 */
class SingleuserSite extends SiteProfileSettings
{
    /**
     * Get the settings for this site profile
     *
     * @return type array  an array of settings
     */
    static function getSettings() {
        global $config;
        return array(
            'singleuser' => array('enabled' => true),
            // We only want to change these values, not replace entire 'site' array
            'site' => array_merge(
                $config['site'], array(
                    'private'    => false,
                    'closed'     => true,
                    'localonly'  => true,
                )
            ),
            'plugins' => array(
                'core'    => self::corePlugins(),
                'default' => array_merge(self::defaultPlugins(), array(
                    'MobileProfile'           => array(),
                )),
                'disable-Directory' => 1,
            ),
            'public'    => array('localonly' => true),
            'discovery' => array('cors' => true) // Allow Cross-Origin Resource Sharing for service discovery (host-meta, XRD, etc.)
        );
    }

}


code:
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, 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/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Queue handler for pushing new notices to local subscribers using SMS.
 */
class SmsQueueHandler extends QueueHandler
{
    function transport()
    {
        return 'sms';
    }

    function handle($notice)
    {
    	require_once(INSTALLDIR.'/lib/mail.php');
        return mail_broadcast_notice_sms($notice);
    }
}


code:
<?php
/*
 * StatusNet - the 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/>.
 */

/**
 * Base class for daemon that can launch one or more processing threads,
 * respawning them if they exit.
 *
 * This is mainly intended for indefinite workloads such as monitoring
 * a queue or maintaining an IM channel.
 *
 * Child classes should implement the
 *
 * We can then pass individual items through the QueueHandler subclasses
 * they belong to. We additionally can handle queues for multiple sites.
 *
 * @package QueueHandler
 * @author Brion Vibber <brion@status.net>
 */
abstract class SpawningDaemon extends Daemon
{
    protected $threads=1;

    const EXIT_OK = 0;
    const EXIT_ERR = 1;
    const EXIT_SHUTDOWN = 100;
    const EXIT_RESTART = 101;

    function __construct($id=null, $daemonize=true, $threads=1)
    {
        parent::__construct($daemonize);

        if ($id) {
            $this->set_id($id);
        }
        $this->threads = $threads;
    }

    /**
     * Perform some actual work!
     *
     * @return int exit code; use self::EXIT_SHUTDOWN to request not to respawn.
     */
    public abstract function runThread();

    /**
     * Spawn one or more background processes and let them start running.
     * Each individual process will execute whatever's in the runThread()
     * method, which should be overridden.
     *
     * Child processes will be automatically respawned when they exit.
     *
     * @todo possibly allow for not respawning on "normal" exits...
     *       though ParallelizingDaemon is probably better for workloads
     *       that have forseeable endpoints.
     */
    function run()
    {
        $this->initPipes();

        $children = array();
        for ($i = 1; $i <= $this->threads; $i++) {
            $pid = pcntl_fork();
            if ($pid < 0) {
                $this->log(LOG_ERROR, "Couldn't fork for thread $i; aborting\n");
                exit(1);
            } else if ($pid == 0) {
                $this->initAndRunChild($i);
            } else {
                $this->log(LOG_INFO, "Spawned thread $i as pid $pid");
                $children[$i] = $pid;
            }
            sleep(common_config('queue', 'spawndelay'));
        }

        $this->log(LOG_INFO, "Waiting for children to complete.");
        while (count($children) > 0) {
            $status = null;
            $pid = pcntl_wait($status);
            if ($pid > 0) {
                $i = array_search($pid, $children);
                if ($i === false) {
                    $this->log(LOG_ERR, "Ignoring exit of unrecognized child pid $pid");
                    continue;
                }
                if (pcntl_wifexited($status)) {
                    $exitCode = pcntl_wexitstatus($status);
                    $info = "status $exitCode";
                } else if (pcntl_wifsignaled($status)) {
                    $exitCode = self::EXIT_ERR;
                    $signal = pcntl_wtermsig($status);
                    $info = "signal $signal";
                }
                unset($children[$i]);

                if ($this->shouldRespawn($exitCode)) {
                    $this->log(LOG_INFO, "Thread $i pid $pid exited with $info; respawing.");

                    $pid = pcntl_fork();
                    if ($pid < 0) {
                        $this->log(LOG_ERROR, "Couldn't fork to respawn thread $i; aborting thread.\n");
                    } else if ($pid == 0) {
                        $this->initAndRunChild($i);
                    } else {
                        $this->log(LOG_INFO, "Respawned thread $i as pid $pid");
                        $children[$i] = $pid;
                    }
                    sleep(common_config('queue', 'spawndelay'));
                } else {
                    $this->log(LOG_INFO, "Thread $i pid $pid exited with status $exitCode; closing out thread.");
                }
            }
        }
        $this->log(LOG_INFO, "All child processes complete.");
        return true;
    }

    /**
     * Create an IPC socket pair which child processes can use to detect
     * if the parent process has been killed.
     */
    function initPipes()
    {
        $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
        if ($sockets) {
            $this->parentWriter = $sockets[0];
            $this->parentReader = $sockets[1];
        } else {
            $this->log(LOG_ERROR, "Couldn't create inter-process sockets");
            exit(1);
        }
    }

    /**
     * Build an IOManager that simply ensures that we have a connection
     * to the parent process open. If it breaks, the child process will
     * die.
     *
     * @return ProcessManager
     */
    public function processManager()
    {
        return new ProcessManager($this->parentReader);
    }

    /**
     * Determine whether to respawn an exited subprocess based on its exit code.
     * Otherwise we'll respawn all exits by default.
     *
     * @param int $exitCode
     * @return boolean true to respawn
     */
    protected function shouldRespawn($exitCode)
    {
        if ($exitCode == self::EXIT_SHUTDOWN) {
            // Thread requested a clean shutdown.
            return false;
        } else {
            // Otherwise we should always respawn!
            return true;
        }
    }

    /**
     * Initialize things for a fresh thread, call runThread(), and
     * exit at completion with appropriate return value.
     */
    protected function initAndRunChild($thread)
    {
        // Close the writer end of our parent<->children pipe.
        fclose($this->parentWriter);
        $this->set_id($this->get_id() . "." . $thread);
        $this->resetDb();
        $exitCode = $this->runThread();
        exit($exitCode);
    }

    /**
     * Reconnect to the database for each child process,
     * or they'll get very confused trying to use the
     * same socket.
     */
    protected function resetDb()
    {
        // @fixme do we need to explicitly open the db too
        // or is this implied?
        global $_DB_DATAOBJECT;
        unset($_DB_DATAOBJECT['CONNECTIONS']);

        // Reconnect main memcached, or threads will stomp on
        // each other and corrupt their requests.
        $cache = Cache::instance();
        if ($cache) {
            $cache->reconnect();
        }

        // Also reconnect memcached for status_network table.
        if (!empty(Status_network::$cache)) {
            Status_network::$cache->close();
            Status_network::$cache = null;
        }
    }

    function log($level, $msg)
    {
        common_log($level, get_class($this) . ' ('. $this->get_id() .'): '.$msg);
    }

    function name()
    {
        return strtolower(get_class($this).'.'.$this->get_id());
    }
}


code:
<?php

if (!defined('GNUSOCIAL')) { exit(1); }

/**
 * Backwards compatible class for plugins for GNU social <1.2
 * and thus only have the class StatusNet defined.
 */
class StatusNet
{
    public static function getActivePlugins()
    {
        return GNUsocial::getActivePlugins();
    }

    public static function isHTTPS()
    {
        return GNUsocial::isHTTPS();
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Abstract class for queue managers
 *
 * PHP version 5
 *
 * LICENCE: 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/>.
 *
 * @category  QueueManager
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

require_once 'Stomp.php';
require_once 'Stomp/Exception.php';

class StompQueueManager extends QueueManager
{
    protected $servers;
    protected $username;
    protected $password;
    protected $base;
    protected $control;

    protected $useTransactions;
    protected $useAcks;

    protected $sites = array();
    protected $subscriptions = array();

    protected $cons = array(); // all open connections
    protected $disconnect = array();
    protected $transaction = array();
    protected $transactionCount = array();
    protected $defaultIdx = 0;

    function __construct()
    {
        parent::__construct();
        $server = common_config('queue', 'stomp_server');
        if (is_array($server)) {
            $this->servers = $server;
        } else {
            $this->servers = array($server);
        }
        $this->username        = common_config('queue', 'stomp_username');
        $this->password        = common_config('queue', 'stomp_password');
        $this->base            = common_config('queue', 'queue_basename');
        $this->control         = common_config('queue', 'control_channel');
        $this->breakout        = common_config('queue', 'breakout');
        $this->useTransactions = common_config('queue', 'stomp_transactions');
        $this->useAcks         = common_config('queue', 'stomp_acks');
    }

    /**
     * Tell the i/o master we only need a single instance to cover
     * all sites running in this process.
     */
    public static function multiSite()
    {
        return IoManager::INSTANCE_PER_PROCESS;
    }

    /**
     * Optional; ping any running queue handler daemons with a notification
     * such as announcing a new site to handle or requesting clean shutdown.
     * This avoids having to restart all the daemons manually to update configs
     * and such.
     *
     * Currently only relevant for multi-site queue managers such as Stomp.
     *
     * @param string $event event key
     * @param string $param optional parameter to append to key
     * @return boolean success
     */
    public function sendControlSignal($event, $param='')
    {
        $message = $event;
        if ($param != '') {
            $message .= ':' . $param;
        }
        $this->_connect();
        $con = $this->cons[$this->defaultIdx];
        $result = $con->send($this->control,
                             $message,
                             array ('created' => common_sql_now()));
        if ($result) {
            $this->_log(LOG_INFO, "Sent control ping to queue daemons: $message");
            return true;
        } else {
            $this->_log(LOG_ERR, "Failed sending control ping to queue daemons: $message");
            return false;
        }
    }

    /**
     * Saves an object into the queue item table.
     *
     * @param mixed $object
     * @param string $queue
     * @param string $siteNickname optional override to drop into another site's queue
     *
     * @return boolean true on success
     * @throws StompException on connection or send error
     */
    public function enqueue($object, $queue, $siteNickname=null)
    {
        $this->_connect();
        if (common_config('queue', 'stomp_enqueue_on')) {
            // We're trying to force all writes to a single server.
            // WARNING: this might do odd things if that server connection dies.
            $idx = array_search(common_config('queue', 'stomp_enqueue_on'),
                                $this->servers);
            if ($idx === false) {
                common_log(LOG_ERR, 'queue stomp_enqueue_on setting does not match our server list.');
                $idx = $this->defaultIdx;
            }
        } else {
            $idx = $this->defaultIdx;
        }
        return $this->_doEnqueue($object, $queue, $idx, $siteNickname);
    }

    /**
     * Saves a notice object reference into the queue item table
     * on the given connection.
     *
     * @return boolean true on success
     * @throws StompException on connection or send error
     */
    protected function _doEnqueue($object, $queue, $idx, $siteNickname=null)
    {
        $rep = $this->logrep($object);
        $envelope = array('site' => $siteNickname ? $siteNickname : common_config('site', 'nickname'),
                          'handler' => $queue,
                          'payload' => $this->encode($object));
        $msg = serialize($envelope);

        $props = array('created' => common_sql_now());
        if ($this->isPersistent($queue)) {
            $props['persistent'] = 'true';
        }

        $con = $this->cons[$idx];
        $host = $con->getServer();
        $target = $this->queueName($queue);
        $result = $con->send($target, $msg, $props);

        if (!$result) {
            $this->_log(LOG_ERR, "Error sending $rep to $queue queue on $host $target");
            return false;
        }

        $this->_log(LOG_DEBUG, "complete remote queueing $rep for $queue on $host $target");
        $this->stats('enqueued', $queue);
        return true;
    }

    /**
     * Determine whether messages to this queue should be marked as persistent.
     * Actual persistent storage depends on the queue server's configuration.
     * @param string $queue
     * @return bool
     */
    protected function isPersistent($queue)
    {
        $mode = common_config('queue', 'stomp_persistent');
        if (is_array($mode)) {
            return in_array($queue, $mode);
        } else {
            return (bool)$mode;
        }
    }

    /**
     * Send any sockets we're listening on to the IO manager
     * to wait for input.
     *
     * @return array of resources
     */
    public function getSockets()
    {
        $sockets = array();
        foreach ($this->cons as $con) {
            if ($con) {
                $sockets[] = $con->getSocket();
            }
        }
        return $sockets;
    }

    /**
     * Get the Stomp connection object associated with the given socket.
     * @param resource $socket
     * @return int index into connections list
     * @throws Exception
     */
    protected function connectionFromSocket($socket)
    {
        foreach ($this->cons as $i => $con) {
            if ($con && $con->getSocket() === $socket) {
                return $i;
            }
        }
        throw new Exception(__CLASS__ . " asked to read from unrecognized socket");
    }

    /**
     * We've got input to handle on our socket!
     * Read any waiting Stomp frame(s) and process them.
     *
     * @param resource $socket
     * @return boolean ok on success
     */
    public function handleInput($socket)
    {
        $idx = $this->connectionFromSocket($socket);
        $con = $this->cons[$idx];
        $host = $con->getServer();
        $this->defaultIdx = $idx;

        $ok = true;
        try {
            $frames = $con->readFrames();
        } catch (StompException $e) {
            $this->_log(LOG_ERR, "Lost connection to $host: " . $e->getMessage());
            fclose($socket); // ???
            $this->cons[$idx] = null;
            $this->transaction[$idx] = null;
            $this->disconnect[$idx] = time();
            return false;
        }
        foreach ($frames as $frame) {
            $dest = $frame->headers['destination'];
            if ($dest == $this->control) {
                if (!$this->handleControlSignal($frame)) {
                    // We got a control event that requests a shutdown;
                    // close out and stop handling anything else!
                    break;
                }
            } else {
                $ok = $this->handleItem($frame) && $ok;
            }
            $this->ack($idx, $frame);
            $this->commit($idx);
            $this->begin($idx);
        }
        return $ok;
    }

    /**
     * Attempt to reconnect in background if we lost a connection.
     */
    function idle()
    {
        $now = time();
        foreach ($this->cons as $idx => $con) {
            if (empty($con)) {
                $age = $now - $this->disconnect[$idx];
                if ($age >= 60) {
                    $this->_reconnect($idx);
                }
            }
        }
        return true;
    }

    /**
     * Initialize our connection and subscribe to all the queues
     * we're going to need to handle... If multiple queue servers
     * are configured for failover, we'll listen to all of them.
     *
     * Side effects: in multi-site mode, may reset site configuration.
     *
     * @param IoMaster $master process/event controller
     * @return bool return false on failure
     */
    public function start($master)
    {
        parent::start($master);
        $this->_connectAll();

        foreach ($this->cons as $i => $con) {
            if ($con) {
                $this->doSubscribe($con);
                $this->begin($i);
            }
        }
        return true;
    }

    /**
     * Close out any active connections.
     *
     * @return bool return false on failure
     */
    public function finish()
    {
        // If there are any outstanding delivered messages we haven't processed,
        // free them for another thread to take.
        foreach ($this->cons as $i => $con) {
            if ($con) {
                $this->rollback($i);
                $con->disconnect();
                $this->cons[$i] = null;
            }
        }
        return true;
    }

    /**
     * Lazy open a single connection to Stomp queue server.
     * If multiple servers are configured, we let the Stomp client library
     * worry about finding a working connection among them.
     */
    protected function _connect()
    {
        if (empty($this->cons)) {
            $list = $this->servers;
            if (count($list) > 1) {
                shuffle($list); // Randomize to spread load
                $url = 'failover://(' . implode(',', $list) . ')';
            } else {
                $url = $list[0];
            }
            $con = $this->_doConnect($url);
            $this->cons = array($con);
            $this->transactionCount = array(0);
            $this->transaction = array(null);
            $this->disconnect = array(null);
        }
    }

    /**
     * Lazy open connections to all Stomp servers, if in manual failover
     * mode. This means the queue servers don't speak to each other, so
     * we have to listen to all of them to make sure we get all events.
     */
    protected function _connectAll()
    {
        if (!common_config('queue', 'stomp_manual_failover')) {
            return $this->_connect();
        }
        if (empty($this->cons)) {
            $this->cons = array();
            $this->transactionCount = array();
            $this->transaction = array();
            foreach ($this->servers as $idx => $server) {
                try {
                    $this->cons[] = $this->_doConnect($server);
                    $this->disconnect[] = null;
                } catch (Exception $e) {
                    // s'okay, we'll live
                    $this->cons[] = null;
                    $this->disconnect[] = time();
                }
                $this->transactionCount[] = 0;
                $this->transaction[] = null;
            }
            if (empty($this->cons)) {
                throw new ServerException("No queue servers reachable...");
                return false;
            }
        }
    }

    /**
     * Attempt to manually reconnect to the Stomp server for the given
     * slot. If successful, set up our subscriptions on it.
     */
    protected function _reconnect($idx)
    {
        try {
            $con = $this->_doConnect($this->servers[$idx]);
        } catch (Exception $e) {
            $this->_log(LOG_ERR, $e->getMessage());
            $con = null;
        }
        if ($con) {
            $this->cons[$idx] = $con;
            $this->disconnect[$idx] = null;

            $this->doSubscribe($con);
            $this->begin($idx);
        } else {
            // Try again later...
            $this->disconnect[$idx] = time();
        }
    }

    protected function _doConnect($server)
    {
        $this->_log(LOG_INFO, "Connecting to '$server' as '$this->username'...");
        $con = new LiberalStomp($server);

        if ($con->connect($this->username, $this->password)) {
            $this->_log(LOG_INFO, "Connected.");
        } else {
            $this->_log(LOG_ERR, 'Failed to connect to queue server');
            throw new ServerException('Failed to connect to queue server');
        }

        return $con;
    }

    /**
     * Set up all our raw queue subscriptions on the given connection
     * @param LiberalStomp $con
     */
    protected function doSubscribe(LiberalStomp $con)
    {
        $host = $con->getServer();
        foreach ($this->subscriptions() as $sub) {
            $this->_log(LOG_INFO, "Subscribing to $sub on $host");
            $con->subscribe($sub);
        }
    }

    /**
     * Grab a full list of stomp-side queue subscriptions.
     * Will include:
     *  - control broadcast channel
     *  - shared group queues for active groups
     *  - per-handler and per-site breakouts from $config['queue']['breakout']
     *    that are rooted in the active groups.
     *
     * @return array of strings
     */
    protected function subscriptions()
    {
        $subs = array();
        $subs[] = $this->control;

        foreach ($this->activeGroups as $group) {
            $subs[] = $this->base . $group;
        }

        foreach ($this->breakout as $spec) {
            $parts = explode('/', $spec);
            if (count($parts) < 2 || count($parts) > 3) {
                common_log(LOG_ERR, "Bad queue breakout specifier $spec");
            }
            if (in_array($parts[0], $this->activeGroups)) {
                $subs[] = $this->base . $spec;
            }
        }
        return array_unique($subs);
    }

    /**
     * Handle and acknowledge an event that's come in through a queue.
     *
     * If the queue handler reports failure, the message is requeued for later.
     * Missing notices or handler classes will drop the message.
     *
     * Side effects: in multi-site mode, may reset site configuration to
     * match the site that queued the event.
     *
     * @param StompFrame $frame
     * @return bool success
     */
    protected function handleItem($frame)
    {
        $host = $this->cons[$this->defaultIdx]->getServer();
        $message = unserialize($frame->body);

        if ($message === false) {
            $this->_log(LOG_ERR, "Can't unserialize frame: {$frame->body}");
            $this->_log(LOG_ERR, "Unserializable frame length: " . strlen($frame->body));
            return false;
        }

        $site = $message['site'];
        $queue = $message['handler'];

        if ($this->isDeadletter($frame, $message)) {
            $this->stats('deadletter', $queue);
	        return false;
        }

        // @fixme detect failing site switches
        $this->switchSite($site);

        try {
            $item = $this->decode($message['payload']);
        } catch (Exception $e) {
            $this->_log(LOG_ERR, "Skipping empty or deleted item in queue $queue from $host");
            $this->stats('baditem', $queue);
            return false;
        }
        $info = $this->logrep($item) . " posted at " .
                $frame->headers['created'] . " in queue $queue from $host";
        $this->_log(LOG_DEBUG, "Dequeued $info");

        $handler = $this->getHandler($queue);
        if (!$handler) {
            $this->_log(LOG_ERR, "Missing handler class; skipping $info");
            $this->stats('badhandler', $queue);
            return false;
        }

        try {
            $ok = $handler->handle($item);
        } catch (Exception $e) {
            $this->_log(LOG_ERR, "Exception on queue $queue: " . $e->getMessage());
            $ok = false;
        }

        if ($ok) {
            $this->_log(LOG_INFO, "Successfully handled $info");
            $this->stats('handled', $queue);
        } else {
            $this->_log(LOG_WARNING, "Failed handling $info");
            // Requeing moves the item to the end of the line for its next try.
            // @fixme add a manual retry count
            $this->enqueue($item, $queue);
            $this->stats('requeued', $queue);
        }

        return $ok;
    }

    /**
     * Check if a redelivered message has been run through enough
     * that we're going to give up on it.
     *
     * @param StompFrame $frame
     * @param array $message unserialized message body
     * @return boolean true if we should discard
     */
    protected function isDeadLetter($frame, $message)
    {
        if (isset($frame->headers['redelivered']) && $frame->headers['redelivered'] == 'true') {
	        // Message was redelivered, possibly indicating a previous failure.
            $msgId = $frame->headers['message-id'];
            $site = $message['site'];
            $queue = $message['handler'];
	        $msgInfo = "message $msgId for $site in queue $queue";

	        $deliveries = $this->incDeliveryCount($msgId);
	        if ($deliveries > common_config('queue', 'max_retries')) {
		        $info = "DEAD-LETTER FILE: Gave up after retry $deliveries on $msgInfo";

		        $outdir = common_config('queue', 'dead_letter_dir');
		        if ($outdir) {
    		        $filename = $outdir . "/$site-$queue-" . rawurlencode($msgId);
    		        $info .= ": dumping to $filename";
    		        file_put_contents($filename, $message['payload']);
		        }

		        common_log(LOG_ERR, $info);
		        return true;
	        } else {
	            common_log(LOG_INFO, "retry $deliveries on $msgInfo");
	        }
        }
        return false;
    }

    /**
     * Update count of times we've re-encountered this message recently,
     * triggered when we get a message marked as 'redelivered'.
     *
     * Requires a CLI-friendly cache configuration.
     *
     * @param string $msgId message-id header from message
     * @return int number of retries recorded
     */
    function incDeliveryCount($msgId)
    {
	    $count = 0;
	    $cache = Cache::instance();
	    if ($cache) {
		    $key = 'statusnet:stomp:message-retries:' . $msgId;
		    $count = $cache->increment($key);
		    if (!$count) {
			    $count = 1;
			    $cache->set($key, $count, null, 3600);
			    $got = $cache->get($key);
		    }
	    }
	    return $count;
    }

    /**
     * Process a control signal broadcast.
     *
     * @param int $idx connection index
     * @param array $frame Stomp frame
     * @return bool true to continue; false to stop further processing.
     */
    protected function handleControlSignal($idx, $frame)
    {
        $message = trim($frame->body);
        if (strpos($message, ':') !== false) {
            list($event, $param) = explode(':', $message, 2);
        } else {
            $event = $message;
            $param = '';
        }

        $shutdown = false;

        if ($event == 'shutdown') {
            $this->master->requestShutdown();
            $shutdown = true;
        } else if ($event == 'restart') {
            $this->master->requestRestart();
            $shutdown = true;
        } else if ($event == 'update') {
            $this->updateSiteConfig($param);
        } else {
            $this->_log(LOG_ERR, "Ignoring unrecognized control message: $message");
        }
        return $shutdown;
    }

    /**
     * Switch site, if necessary, and reset current handler assignments
     * @param string $site
     */
    function switchSite($site)
    {
        if ($site != GNUsocial::currentSite()) {
            $this->stats('switch');
            GNUsocial::switchSite($site);
            $this->initialize();
        }
    }

    /**
     * (Re)load runtime configuration for a given site by nickname,
     * triggered by a broadcast to the 'statusnet-control' topic.
     *
     * Configuration changes in database should update, but config
     * files might not.
     *
     * @param array $frame Stomp frame
     * @return bool true to continue; false to stop further processing.
     */
    protected function updateSiteConfig($nickname)
    {
        $sn = Status_network::getKV('nickname', $nickname);
        if ($sn) {
            $this->switchSite($nickname);
            if (!in_array($nickname, $this->sites)) {
                $this->addSite();
            }
            $this->stats('siteupdate');
        } else {
            $this->_log(LOG_ERR, "Ignoring ping for unrecognized new site $nickname");
        }
    }

    /**
     * Combines the queue_basename from configuration with the
     * group name for this queue to give eg:
     *
     * /queue/statusnet/main
     * /queue/statusnet/main/distrib
     * /queue/statusnet/xmpp/xmppout/site01
     *
     * @param string $queue
     * @return string
     */
    protected function queueName($queue)
    {
        $group = $this->queueGroup($queue);
        $site = GNUsocial::currentSite();

        $specs = array("$group/$queue/$site",
                       "$group/$queue");
        foreach ($specs as $spec) {
            if (in_array($spec, $this->breakout)) {
                return $this->base . $spec;
            }
        }
        return $this->base . $group;
    }

    /**
     * Get the breakout mode for the given queue on the current site.
     *
     * @param string $queue
     * @return string one of 'shared', 'handler', 'site'
     */
    protected function breakoutMode($queue)
    {
        $breakout = common_config('queue', 'breakout');
        if (isset($breakout[$queue])) {
            return $breakout[$queue];
        } else if (isset($breakout['*'])) {
            return $breakout['*'];
        } else {
            return 'shared';
        }
    }

    protected function begin($idx)
    {
        if ($this->useTransactions) {
            if (!empty($this->transaction[$idx])) {
                throw new Exception("Tried to start transaction in the middle of a transaction");
            }
            $this->transactionCount[$idx]++;
            $this->transaction[$idx] = $this->master->id . '-' . $this->transactionCount[$idx] . '-' . time();
            $this->cons[$idx]->begin($this->transaction[$idx]);
        }
    }

    protected function ack($idx, $frame)
    {
        if ($this->useAcks) {
            if ($this->useTransactions) {
                if (empty($this->transaction[$idx])) {
                    throw new Exception("Tried to ack but not in a transaction");
                }
                $this->cons[$idx]->ack($frame, $this->transaction[$idx]);
            } else {
                $this->cons[$idx]->ack($frame);
            }
        }
    }

    protected function commit($idx)
    {
        if ($this->useTransactions) {
            if (empty($this->transaction[$idx])) {
                throw new Exception("Tried to commit but not in a transaction");
            }
            $this->cons[$idx]->commit($this->transaction[$idx]);
            $this->transaction[$idx] = null;
        }
    }

    protected function rollback($idx)
    {
        if ($this->useTransactions) {
            if (empty($this->transaction[$idx])) {
                throw new Exception("Tried to rollback but not in a transaction");
            }
            $this->cons[$idx]->commit($this->transaction[$idx]);
            $this->transaction[$idx] = null;
        }
    }
}