code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for inviting collegues and friends
 *
 * 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    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('STATUSNET')) {
    exit(1);
}

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

/**
 * Form for inviting collegues and friends
 *
 * @category Form
 * @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/
 */
class InviteForm extends Form
{
    /**
     * Constructor
     *
     * @param Action $out output channel
     */
    function __construct($out=null)
    {
        parent::__construct($out);
    }

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
       return 'form_invite';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('invite');
    }

    /**
     * Name of the form
     *
     * @return void
     */
    function formLegend()
    {
        // TRANS: Form legend.
        $this->out->element('legend', null, _('Invite collegues'));
    }

    /**
     * Data elements of the form
     *
     * @return void
     */
    function formData()
    {
        $this->out->elementStart('ul', 'form_data');
        $this->out->elementStart('li');
        $this->out->textarea(
            'addresses',
            // TRANS: Field label for a list of e-mail addresses.
            _('Email addresses'),
            $this->out->trimmed('addresses'),
            // TRANS: Field title for a list of e-mail addresses.
            _('Addresses of friends to invite (one per line).')
        );
        $this->out->elementEnd('li');
        $this->out->elementStart('li');
        $this->out->textarea(
            // TRANS: Field label for a personal message to send to invitees.
            'personal', _('Personal message'),
            $this->out->trimmed('personal'),
            // TRANS: Field title for a personal message to send to invitees.
            _('Optionally add a personal message to the invitation.')
        );
        $this->out->elementEnd('li');
        $this->out->elementEnd('ul');
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        $this->out->submit(
            'send',
            // TRANS: Send button for inviting friends
            _m('BUTTON','Send'), 'submit form_action-primary',
            'send',
            // TRANS: Submit button title.
            _('Send invitations.')
        );
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Abstract class for i/o 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>
 * @author    Brion Vibber <brion@status.net>
 * @copyright 2009-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/
 */

abstract class IoManager
{
    const SINGLE_ONLY = 0;
    const INSTANCE_PER_SITE = 1;
    const INSTANCE_PER_PROCESS = 2;

    /**
     * Factory function to get an appropriate subclass.
     */
    public static function get() {
        throw new MethodNotImplementedException(__METHOD__);
    }

    /**
     * Tell the i/o queue master if and how we can handle multi-site
     * processes.
     *
     * Return one of:
     *   IoManager::SINGLE_ONLY
     *   IoManager::INSTANCE_PER_SITE
     *   IoManager::INSTANCE_PER_PROCESS
     */
    public static function multiSite()
    {
        return IoManager::SINGLE_ONLY;
    }

    /**
     * If in a multisite configuration, the i/o master will tell
     * your manager about each site you'll have to handle so you
     * can do any necessary per-site setup.
     *
     * The new site will be the currently live configuration during
     * this call.
     */
    public function addSite()
    {
        /* no-op */
    }

    /**
     * This method is called when data is available on one of your
     * i/o manager's sockets. The socket with data is passed in,
     * in case you have multiple sockets.
     *
     * If your i/o manager is based on polling during idle processing,
     * you don't need to implement this.
     *
     * @param resource $socket
     * @return boolean true on success, false on failure
     */
    public function handleInput($socket)
    {
        return true;
    }

    /**
     * Return any open sockets that the run loop should listen
     * for input on. If input comes in on a listed socket,
     * the matching manager's handleInput method will be called.
     *
     * @return array of resources
     */
    function getSockets()
    {
        return array();
    }

    /**
     * Maximum planned time between poll() calls when input isn't waiting.
     * Actual time may vary!
     *
     * When we get a polling hit, the timeout will be cut down to 0 while
     * input is coming in, then will back off to this amount if no further
     * input shows up.
     *
     * By default polling is disabled; you must override this to enable
     * polling for this manager.
     *
     * @return int max poll interval in seconds, or 0 to disable polling
     */
    function pollInterval()
    {
        return 0;
    }

    /**
     * Request a maximum timeout for listeners before the next idle period.
     * Actual wait may be shorter, so don't go crazy in your idle()!
     * Wait could be longer if other handlers performed some slow activity.
     *
     * Return 0 to request that listeners return immediately if there's no
     * i/o and speed up the idle as much as possible; but don't do that all
     * the time as this will burn CPU.
     *
     * @return int seconds
     */
    function timeout()
    {
        return 60;
    }

    /**
     * Called by IoManager after each handled item or empty polling cycle.
     * This is a good time to e.g. service your XMPP connection.
     *
     * Doesn't need to be overridden if there's no maintenance to do.
     */
    function idle()
    {
        return true;
    }

    /**
     * The meat of a polling manager... check for something to do
     * and do it! Note that you should not take too long, as other
     * i/o managers may need to do some work too!
     *
     * On a successful hit, the next poll() call will come as soon
     * as possible followed by exponential backoff up to pollInterval()
     * if no more data is available.
     *
     * @return boolean true if events were hit
     */
    public function poll()
    {
        return false;
    }

    /**
     * Initialization, run when the queue manager starts.
     * If this function indicates failure, the handler run will be aborted.
     *
     * @param IoMaster $master process/event controller
     * @return boolean true on success, false on failure
     */
    public function start($master)
    {
        $this->master = $master;
        return true;
    }

    /**
     * Cleanup, run when the queue manager ends.
     * If this function indicates failure, a warning will be logged.
     *
     * @return boolean true on success, false on failure
     */
    public function finish()
    {
        return true;
    }

    /**
     * Ping iomaster's queue status monitor with a stats update.
     * Only valid during input loop!
     *
     * @param string $counter keyword for counter to increment
     */
    public function stats($counter, $owners=array())
    {
        $this->master->stats($counter, $owners);
    }
}

repo_name: piratas/tortuga

license: mit


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * I/O manager to wrap around socket-reading and polling queue & connection 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    Brion Vibber <brion@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/
 */

abstract class IoMaster
{
    public $id;

    protected $multiSite = false;
    protected $managers = array();
    protected $singletons = array();

    protected $pollTimeouts = array();
    protected $lastPoll = array();

    public $shutdown = false; // Did we do a graceful shutdown?
    public $respawn = true; // Should we respawn after shutdown?

    /**
     * @param string $id process ID to use in logging/monitoring
     */
    public function __construct($id)
    {
        $this->id = $id;
        $this->monitor = new QueueMonitor();
    }

    public function init($multiSite=null)
    {
        if ($multiSite !== null) {
            $this->multiSite = $multiSite;
        }

        $this->initManagers();
    }

    /**
     * Initialize IoManagers which are appropriate to this instance;
     * pass class names or instances into $this->instantiate().
     *
     * If setup and configuration may vary between sites in multi-site
     * mode, it's the subclass's responsibility to set them up here.
     *
     * Switching site configurations is an acceptable side effect.
     */
    abstract function initManagers();

    /**
     * Instantiate an i/o manager class for the current site.
     * If a multi-site capable handler is already present,
     * we don't need to build a new one.
     *
     * @param mixed $manager class name (to run $class::get()) or object
     */
    protected function instantiate($manager)
    {
        if (is_string($manager)) {
            $manager = call_user_func(array($class, 'get'));
        }

        $caps = $manager->multiSite();
        if ($caps == IoManager::SINGLE_ONLY) {
            if ($this->multiSite) {
                throw new Exception("$class can't run with --all; aborting.");
            }
        } else if ($caps == IoManager::INSTANCE_PER_PROCESS) {
            $manager->addSite();
        }

        if (!in_array($manager, $this->managers, true)) {
            // Only need to save singletons once
            $this->managers[] = $manager;
        }
    }

    /**
     * Basic run loop...
     *
     * Initialize all io managers, then sit around waiting for input.
     * Between events or timeouts, pass control back to idle() method
     * to allow for any additional background processing.
     */
    function service()
    {
        $this->logState('init');
        $this->start();
        $this->checkMemory(false);

        while (!$this->shutdown) {
            $timeouts = array_values($this->pollTimeouts);
            $timeouts[] = 60; // default max timeout

            // Wait for something on one of our sockets
            $sockets = array();
            $managers = array();
            foreach ($this->managers as $manager) {
                foreach ($manager->getSockets() as $socket) {
                    $sockets[] = $socket;
                    $managers[] = $manager;
                }
                $timeouts[] = intval($manager->timeout());
            }

            $timeout = min($timeouts);
            if ($sockets) {
                $read = $sockets;
                $write = array();
                $except = array();
                $this->logState('listening');
                //common_debug("Waiting up to $timeout seconds for socket data...");
                $ready = stream_select($read, $write, $except, $timeout, 0);

                if ($ready === false) {
                    common_log(LOG_ERR, "Error selecting on sockets");
                } else if ($ready > 0) {
                    foreach ($read as $socket) {
                        $index = array_search($socket, $sockets, true);
                        if ($index !== false) {
                            $this->logState('queue');
                            $managers[$index]->handleInput($socket);
                        } else {
                            common_log(LOG_ERR, "Saw input on a socket we didn't listen to");
                        }
                    }
                }
            }

            if ($timeout > 0 && empty($sockets)) {
                // If we had no listeners, sleep until the pollers' next requested wakeup.
                common_log(LOG_DEBUG, "Sleeping $timeout seconds until next poll cycle...");
                $this->logState('sleep');
                sleep($timeout);
            }

            $this->logState('poll');
            $this->poll();

            $this->logState('idle');
            $this->idle();

            $this->checkMemory();
        }

        $this->logState('shutdown');
        $this->finish();
    }

    /**
     * Check runtime memory usage, possibly triggering a graceful shutdown
     * and thread respawn if we've crossed the soft limit.
     *
     * @param boolean $respawn if false we'll shut down instead of respawning
     */
    protected function checkMemory($respawn=true)
    {
        $memoryLimit = $this->softMemoryLimit();
        if ($memoryLimit > 0) {
            $usage = memory_get_usage();
            if ($usage > $memoryLimit) {
                common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
                if ($respawn) {
                    $this->requestRestart();
                } else {
                    $this->requestShutdown();
                }
            } else if (common_config('queue', 'debug_memory')) {
                $fmt = number_format($usage);
                common_log(LOG_DEBUG, "Memory usage $fmt");
            }
        }
    }

    /**
     * Return fully-parsed soft memory limit in bytes.
     * @return intval 0 or -1 if not set
     */
    function softMemoryLimit()
    {
        $softLimit = trim(common_config('queue', 'softlimit'));
        if (substr($softLimit, -1) == '%') {
            $limit = $this->parseMemoryLimit(ini_get('memory_limit'));
            if ($limit > 0) {
                return intval(substr($softLimit, 0, -1) * $limit / 100);
            } else {
                return -1;
            }
        } else {
            return $this->parseMemoryLimit($softLimit);
        }
        return $softLimit;
    }

    /**
     * Interpret PHP shorthand for memory_limit and friends.
     * Why don't they just expose the actual numeric value? :P
     * @param string $mem
     * @return int
     */
    public function parseMemoryLimit($mem)
    {
        // http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
        $mem = strtolower(trim($mem));
        $size = array('k' => 1024,
                      'm' => 1024*1024,
                      'g' => 1024*1024*1024);
        if (empty($mem)) {
            return 0;
        } else if (is_numeric($mem)) {
            return intval($mem);
        } else {
            $mult = substr($mem, -1);
            if (isset($size[$mult])) {
                return substr($mem, 0, -1) * $size[$mult];
            } else {
                return intval($mem);
            }
        }
    }

    function start()
    {
        foreach ($this->managers as $index => $manager) {
            $manager->start($this);
            // @fixme error check
            if ($manager->pollInterval()) {
                // We'll want to check for input on the first pass
                $this->pollTimeouts[$index] = 0;
                $this->lastPoll[$index] = 0;
            }
        }
    }

    function finish()
    {
        foreach ($this->managers as $manager) {
            $manager->finish();
            // @fixme error check
        }
    }

    /**
     * Called during the idle portion of the runloop to see which handlers
     */
    function poll()
    {
        foreach ($this->managers as $index => $manager) {
            $interval = $manager->pollInterval();
            if ($interval <= 0) {
                // Not a polling manager.
                continue;
            }

            if (isset($this->pollTimeouts[$index])) {
                $timeout = $this->pollTimeouts[$index];
                if (time() - $this->lastPoll[$index] < $timeout) {
                    // Not time to poll yet.
                    continue;
                }
            } else {
                $timeout = 0;
            }
            $hit = $manager->poll();

            $this->lastPoll[$index] = time();
            if ($hit) {
                // Do the next poll quickly, there may be more input!
                $this->pollTimeouts[$index] = 0;
            } else {
                // Empty queue. Exponential backoff up to the maximum poll interval.
                if ($timeout > 0) {
                    $timeout = min($timeout * 2, $interval);
                } else {
                    $timeout = 1;
                }
                $this->pollTimeouts[$index] = $timeout;
            }
        }
    }

    /**
     * Called after each handled item or empty polling cycle.
     * This is a good time to e.g. service your XMPP connection.
     */
    function idle()
    {
        foreach ($this->managers as $manager) {
            $manager->idle();
        }
    }

    /**
     * Send thread state update to the monitoring server, if configured.
     *
     * @param string $state ('init', 'queue', 'shutdown' etc)
     * @param string $substate (optional, eg queue name 'omb' 'sms' etc)
     */
    protected function logState($state, $substate='')
    {
        $this->monitor->logState($this->id, $state, $substate);
    }

    /**
     * Send thread stats.
     * Thread ID will be implicit; other owners can be listed as well
     * for per-queue and per-site records.
     *
     * @param string $key counter name
     * @param array $owners list of owner keys like 'queue:xmpp' or 'site:stat01'
     */
    public function stats($key, $owners=array())
    {
        $owners[] = "thread:" . $this->id;
        $this->monitor->stats($key, $owners);
    }

    /**
     * For IoManagers to request a graceful shutdown at end of event loop.
     */
    public function requestShutdown()
    {
        $this->shutdown = true;
        $this->respawn = false;
    }

    /**
     * For IoManagers to request a graceful restart at end of event loop.
     */
    public function requestRestart()
    {
        $this->shutdown = true;
        $this->respawn = true;
    }

}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for joining a group
 *
 * 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>
 * @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/
 */

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

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

/**
 * Form for joining a group
 *
 * @category Form
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Sarven Capadisli <csarven@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      UnsubscribeForm
 */
class JoinForm extends Form
{
    /**
     * group for user to join
     */
    var $group = null;

    /**
     * Constructor
     *
     * @param HTMLOutputter $out   output channel
     * @param group         $group group to join
     */
    function __construct($out=null, $group=null)
    {
        parent::__construct($out);

        $this->group = $group;
    }

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
        return 'group-join-' . $this->group->id;
    }

    /**
     * class of the form
     *
     * @return string of the form class
     */
    function formClass()
    {
        return 'form_group_join ajax';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('joingroup',
                                array('id' => $this->group->id));
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text for joining a group.
        $this->out->submit('submit', _m('BUTTON','Join'));
    }
}


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  Search
 * @package   StatusNet
 * @author    Zach Copley <zach@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') && !defined('LACONICA')) {
    exit(1);
}

/**
 * widget-like class for showing JSON search results
 *
 * @category Search
 * @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/
 *
 */

class JSONSearchResultsList
{
    protected $notice;  // protected attrs invisible to json_encode()
    protected $rpp;

    // The below attributes are carefully named so the JSON output from
    // this obj matches the output from search.twitter.com

    var $results;
    var $since_id;
    var $max_id;
    var $refresh_url;
    var $results_per_page;
    var $completed_in;
    var $page;
    var $query;

    /**
     * constructor
     *
     * @param Notice $notice   stream of notices from DB_DataObject
     * @param string $query    the original search query
     * @param int    $rpp      the number of results to display per page
     * @param int    $page     a page offset
     * @param int    $since_id only display notices newer than this
     */

    function __construct($notice, $query, $rpp, $page, $since_id = 0)
    {
        $this->notice           = $notice;
        $this->query            = urlencode($query);
        $this->results_per_page = $rpp;
        $this->rpp              = $rpp;
        $this->page             = $page;
        $this->since_id         = $since_id;
        $this->results          = array();
    }

    /**
     * show the list of search results
     *
     * @return int $count of the search results listed.
     */

    function show()
    {
        $cnt = 0;
        $this->max_id = 0;

        $time_start = microtime(true);

        while ($this->notice->fetch() && $cnt <= $this->rpp) {
            $cnt++;

            // XXX: Hmmm. this depends on desc sort order
            if (!$this->max_id) {
                $this->max_id = (int)$this->notice->id;
            }

            if ($this->since_id && $this->notice->id <= $this->since_id) {
                break;
            }

            if ($cnt > $this->rpp) {
                break;
            }

            $profile = $this->notice->getProfile();

            // Don't show notices from deleted users

            if (!empty($profile)) {
                $item = new ResultItem($this->notice);
                array_push($this->results, $item);
            }
        }

        $time_end           = microtime(true);
        $this->completed_in = $time_end - $time_start;

        // Set other attrs

        $this->refresh_url = '?since_id=' . $this->max_id .
            '&q=' . $this->query;

        // pagination stuff

        if ($cnt > $this->rpp) {
            $this->next_page = '?page=' . ($this->page + 1) .
                '&max_id=' . $this->max_id;
            if ($this->rpp != 15) {
                $this->next_page .= '&rpp=' . $this->rpp;
            }
            $this->next_page .= '&q=' . $this->query;
        }

        if ($this->page > 1) {
            $this->previous_page = '?page=' . ($this->page - 1) .
                '&max_id=' . $this->max_id;
            if ($this->rpp != 15) {
                $this->previous_page .= '&rpp=' . $this->rpp;
            }
            $this->previous_page .= '&q=' . $this->query;
        }

        print json_encode($this);

        return $cnt;
    }
}

/**
 * widget for displaying a single JSON search result
 *
 * @category UI
 * @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/
 * @see      JSONSearchResultsList
 */

class ResultItem
{
    /** The notice this item is based on. */

    protected $notice;  // protected attrs invisible to json_encode()

    /** The profile associated with the notice. */

    protected $profile;

    // The below attributes are carefully named so the JSON output from
    // this obj matches the output from search.twitter.com

    var $text;
    var $to_user_id;
    var $to_user;
    var $from_user;
    var $id;
    var $from_user_id;
    var $iso_language_code;
    var $source;
    var $profile_image_url;
    var $created_at;

    /**
     * constructor
     *
     * Also initializes the profile attribute.
     *
     * @param Notice $notice The notice we'll display
     */

    function __construct($notice)
    {
        $this->notice  = $notice;
        $this->profile = $notice->getProfile();
        $this->buildResult();
    }

    /**
     * Build a search result object
     *
     * This populates the the result in preparation for JSON encoding.
     *
     * @return void
     */

    function buildResult()
    {
        $this->text      = $this->notice->content;
        $replier_profile = null;

        if ($this->notice->reply_to) {
            $reply = Notice::getKV(intval($this->notice->reply_to));
            if ($reply) {
                $replier_profile = $reply->getProfile();
            }
        }

        $this->to_user_id = ($replier_profile) ?
            intval($replier_profile->id) : null;
        $this->to_user    = ($replier_profile) ?
            $replier_profile->nickname : null;

        $this->from_user    = $this->profile->nickname;
        $this->id           = $this->notice->id;
        $this->from_user_id = $this->profile->id;

        $this->iso_language_code = Profile_prefs::getConfigData($this->profile, 'site', 'language');

        $this->source = $this->getSourceLink($this->notice->source);

        $this->profile_image_url = $this->profile->avatarUrl(AVATAR_STREAM_SIZE);

        $this->created_at = common_date_rfc2822($this->notice->created);
    }

    /**
     * Show the source of the notice
     *
     * Either the name (and link) of the API client that posted the notice,
     * or one of other other channels.
     *
     * @param string $source the source of the Notice
     *
     * @return string a fully rendered source of the Notice
     */
    function getSourceLink($source)
    {
        // Gettext translations for the below source types are available.
        $source_name = _($source);
        switch ($source) {
        case 'web':
        case 'xmpp':
        case 'mail':
        case 'omb':
        case 'api':
            break;
        default:
            $ns = Notice_source::getKV($source);
            if ($ns instanceof Notice_source) {
                $source_name = 'url . '">' . $ns->name . '';
            }
            break;
        }

        return $source_name;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * utility functions for i18n
 *
 * 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 I18n
 * @package  StatusNet
 * @author   Matthew Gregg <matthew.gregg@gmail.com>
 * @author   Ciaran Gultnieks <ciaran@ciarang.com>
 * @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/
 */

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

// Locale category constants are usually predefined, but may not be
// on some systems such as Win32.
$LC_CATEGORIES = array('LC_CTYPE',
                       'LC_NUMERIC',
                       'LC_TIME',
                       'LC_COLLATE',
                       'LC_MONETARY',
                       'LC_MESSAGES',
                       'LC_ALL');
foreach ($LC_CATEGORIES as $key => $name) {
    if (!defined($name)) {
        define($name, $key);
    }
}

if (!function_exists('gettext')) {
    require_once("php-gettext/gettext.inc");
}

if (!function_exists('dpgettext')) {
    /**
     * Context-aware dgettext wrapper; use when messages in different contexts
     * won't be distinguished from the English source but need different translations.
     * The context string will appear as msgctxt in the .po files.
     *
     * Not currently exposed in PHP's gettext module; implemented to be compat
     * with gettext.h's macros.
     *
     * @param string $domain domain identifier
     * @param string $context context identifier, should be some key like "menu|file"
     * @param string $msgid English source text
     * @return string original or translated message
     */
    function dpgettext($domain, $context, $msg)
    {
        $msgid = $context . "\004" . $msg;
        $out = dcgettext($domain, $msgid, LC_MESSAGES);
        if ($out == $msgid) {
            return $msg;
        } else {
            return $out;
        }
    }
}

if (!function_exists('pgettext')) {
    /**
     * Context-aware gettext wrapper; use when messages in different contexts
     * won't be distinguished from the English source but need different translations.
     * The context string will appear as msgctxt in the .po files.
     *
     * Not currently exposed in PHP's gettext module; implemented to be compat
     * with gettext.h's macros.
     *
     * @param string $context context identifier, should be some key like "menu|file"
     * @param string $msgid English source text
     * @return string original or translated message
     */
    function pgettext($context, $msg)
    {
        return dpgettext(textdomain(NULL), $context, $msg);
    }
}

if (!function_exists('dnpgettext')) {
    /**
     * Context-aware dngettext wrapper; use when messages in different contexts
     * won't be distinguished from the English source but need different translations.
     * The context string will appear as msgctxt in the .po files.
     *
     * Not currently exposed in PHP's gettext module; implemented to be compat
     * with gettext.h's macros.
     *
     * @param string $domain domain identifier
     * @param string $context context identifier, should be some key like "menu|file"
     * @param string $msg singular English source text
     * @param string $plural plural English source text
     * @param int $n number of items to control plural selection
     * @return string original or translated message
     */
    function dnpgettext($domain, $context, $msg, $plural, $n)
    {
        $msgid = $context . "\004" . $msg;
        $out = dcngettext($domain, $msgid, $plural, $n, LC_MESSAGES);
        if ($out == $msgid) {
            return $msg;
        } else {
            return $out;
        }
    }
}

if (!function_exists('npgettext')) {
    /**
     * Context-aware ngettext wrapper; use when messages in different contexts
     * won't be distinguished from the English source but need different translations.
     * The context string will appear as msgctxt in the .po files.
     *
     * Not currently exposed in PHP's gettext module; implemented to be compat
     * with gettext.h's macros.
     *
     * @param string $context context identifier, should be some key like "menu|file"
     * @param string $msg singular English source text
     * @param string $plural plural English source text
     * @param int $n number of items to control plural selection
     * @return string original or translated message
     */
    function npgettext($context, $msg, $plural, $n)
    {
        return dnpgettext(textdomain(NULL), $msgid, $plural, $n, LC_MESSAGES);
    }
}

/**
 * Shortcut for *gettext functions with smart domain detection.
 *
 * If calling from a plugin, this function checks which plugin was
 * being called from and uses that as text domain, which will have
 * been set up during plugin initialization.
 *
 * Also handles plurals and contexts depending on what parameters
 * are passed to it:
 *
 *   gettext -> _m($msg)
 *  ngettext -> _m($msg1, $msg2, $n)
 *  pgettext -> _m($ctx, $msg)
 * npgettext -> _m($ctx, $msg1, $msg2, $n)
 *
 * @fixme may not work properly in eval'd code
 *
 * @param string $msg
 * @return string
 */
function _m($msg/*, ...*/)
{
    $domain = _mdomain(debug_backtrace());
    $args = func_get_args();
    switch(count($args)) {
    case 1: return dgettext($domain, $msg);
    case 2: return dpgettext($domain, $args[0], $args[1]);
    case 3: return dngettext($domain, $args[0], $args[1], $args[2]);
    case 4: return dnpgettext($domain, $args[0], $args[1], $args[2], $args[3]);
    default: throw new Exception("Bad parameter count to _m()");
    }
}

/**
 * Looks for which plugin we've been called from to set the gettext domain;
 * if not in a plugin subdirectory, we'll use the default 'statusnet'.
 *
 * Note: we can't return null for default domain since most of the PHP gettext
 * wrapper functions turn null into "" before passing to the backend library.
 *
 * @param array $backtrace debug_backtrace() output
 * @return string
 * @private
 * @fixme could explode if SN is under a 'plugins' folder or share name.
 */
function _mdomain($backtrace)
{
    /*
      0 =>
        array
          'file' => string '/var/www/mublog/plugins/FeedSub/FeedSubPlugin.php' (length=49)
          'line' => int 77
          'function' => string '_m' (length=2)
          'args' =>
            array
              0 => &string 'Feeds' (length=5)
    */
    static $cached;
    $path = $backtrace[0]['file'];
    if (!isset($cached[$path])) {
        $final = 'statusnet'; // assume default domain
        if (DIRECTORY_SEPARATOR !== '/') {
            $path = strtr($path, DIRECTORY_SEPARATOR, '/');
        }
        $plug = strpos($path, '/plugins/');
        if ($plug === false) {
            // We're not in a plugin; return default domain.
            $final = 'statusnet';
        } else {
            $cut = $plug + 9;
            $cut2 = strpos($path, '/', $cut);
            if ($cut2) {
                $final = substr($path, $cut, $cut2 - $cut);
            } else {
                // We might be running directly from the plugins dir?
                // If so, there's no place to store locale info.
                $final = 'statusnet';
            }
        }
        $cached[$path] = $final;
    }
    return $cached[$path];
}

/**
 * Content negotiation for language codes
 *
 * @param string $httplang HTTP Accept-Language header
 *
 * @return string language code for best language match
 */

function client_prefered_language($httplang)
{
    $client_langs = array();

    $all_languages = common_config('site', 'languages');

    preg_match_all('"(((\S\S)-?(\S\S)?)(;q=([0-9.]+))?)\s*(,\s*|$)"',
                   strtolower($httplang), $httplang);

    for ($i = 0; $i < count($httplang); $i++) {
        if (!empty($httplang[2][$i])) {
            // if no q default to 1.0
            $client_langs[$httplang[2][$i]] =
              ($httplang[6][$i]? (float) $httplang[6][$i] : 1.0 - ($i*0.01));
        }
        if (!empty($httplang[3][$i]) && empty($client_langs[$httplang[3][$i]])) {
            // if a catchall default 0.01 lower
            $client_langs[$httplang[3][$i]] =
              ($httplang[6][$i]? (float) $httplang[6][$i]-0.01 : 0.99);
        }
    }
    // sort in decending q
    arsort($client_langs);

    foreach ($client_langs as $lang => $q) {
        if (isset($all_languages[$lang])) {
            return($all_languages[$lang]['lang']);
        }
    }
    return false;
}

/**
 * returns a simple code -> name mapping for languages
 *
 * @return array map of available languages by code to language name.
 */

function get_nice_language_list()
{
    $nice_lang = array();

    $all_languages = common_config('site', 'languages');

    foreach ($all_languages as $lang) {
        $nice_lang = $nice_lang + array($lang['lang'] => $lang['name']);
    }
    return $nice_lang;
}

/*
 * Check whether a language is right-to-left
 *
 * @param string $lang language code of the language to check
 *
 * @return boolean true if language is rtl
 */

function is_rtl($lang)
{
    $all_languages = common_config('site', 'languages');
    $lang = $all_languages[$lang];
    return ($lang['direction'] == 'rtl');
}

/**
 * Get a list of all languages that are enabled in the default config
 *
 * This should ONLY be called when setting up the default config in common.php.
 * Any other attempt to get a list of languages should instead call
 * common_config('site','languages')
 *
 * @return array mapping of language codes to language info
 */
function get_all_languages() {
    return array(
        'af'      => array('q' => 0.8, 'lang' => 'af', 'name' => 'Afrikaans', 'direction' => 'ltr'),
        'ar'      => array('q' => 0.8, 'lang' => 'ar', 'name' => 'Arabic', 'direction' => 'rtl'),
        'ast'      => array('q' => 1, 'lang' => 'ast', 'name' => 'Asturian', 'direction' => 'ltr'),
        'eu'      => array('q' => 1, 'lang' => 'eu',    'name' => 'Basque', 'direction' => 'ltr'),
        'be-tarask' => array('q' => 0.5, 'lang' => 'be-tarask', 'name' => 'Belarusian (Taraškievica orthography)', 'direction' => 'ltr'),
        'br'      => array('q' => 0.8, 'lang' => 'br', 'name' => 'Breton', 'direction' => 'ltr'),
        'bg'      => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'),
        'my'      => array('q' => 1, 'lang' => 'my', 'name' => 'Burmese', 'direction' => 'ltr'),
        'ca'      => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'),
        'zh-cn'   => array('q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'),
        'zh-hant' => array('q' => 0.2, 'lang' => 'zh_TW', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'),
        'ksh'      => array('q' => 1, 'lang' => 'ksh', 'name' => 'Colognian', 'direction' => 'ltr'),
        'cs'      => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'),
        'da'      => array('q' => 0.8, 'lang' => 'da', 'name' => 'Danish', 'direction' => 'ltr'),
        'nl'      => array('q' => 0.5, 'lang' => 'nl', 'name' => 'Dutch', 'direction' => 'ltr'),
        'arz'     => array('q' => 0.8, 'lang' => 'arz', 'name' => 'Egyptian Spoken Arabic', 'direction' => 'rtl'),
        'en'      => array('q' => 1, 'lang' => 'en',    'name' => 'English', 'direction' => 'ltr'),
        'en-us'   => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'),
        'en-gb'   => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'),
        'eo'      => array('q' => 0.8, 'lang' => 'eo',    'name' => 'Esperanto', 'direction' => 'ltr'),
        'fi'      => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'),
        'fr-fr'   => array('q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'),
        'fur'     => array('q' => 0.8, 'lang' => 'fur', 'name' => 'Friulian', 'direction' => 'ltr'),
        'gl'      => array('q' => 0.8, 'lang' => 'gl', 'name' => 'Galician', 'direction' => 'ltr'),
        'ka'      => array('q' => 0.8, 'lang' => 'ka',    'name' => 'Georgian', 'direction' => 'ltr'),
        'de'      => array('q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'),
        'el'      => array('q' => 0.1, 'lang' => 'el',    'name' => 'Greek', 'direction' => 'ltr'),
        'he'      => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'),
        'hu'      => array('q' => 0.8, 'lang' => 'hu', 'name' => 'Hungarian', 'direction' => 'ltr'),
        'is'      => array('q' => 0.1, 'lang' => 'is', 'name' => 'Icelandic', 'direction' => 'ltr'),
        'id'      => array('q' => 1, 'lang' => 'id', 'name' => 'Indonesian', 'direction' => 'ltr'),
        'ia'      => array('q' => 0.8, 'lang' => 'ia', 'name' => 'Interlingua', 'direction' => 'ltr'),
        'ga'      => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Irish', 'direction' => 'ltr'),
        'it'      => array('q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'),
        'jp'      => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'),
        'ko'      => array('q' => 0.9, 'lang' => 'ko',    'name' => 'Korean', 'direction' => 'ltr'),
        'lv'      => array('q' => 1, 'lang' => 'lv', 'name' => 'Latvian', 'direction' => 'ltr'),
        'lt'      => array('q' => 1, 'lang' => 'lt', 'name' => 'Lithuanian', 'direction' => 'ltr'),
        'lb'      => array('q' => 1, 'lang' => 'lb', 'name' => 'Luxembourgish', 'direction' => 'ltr'),
        'mk'      => array('q' => 0.5, 'lang' => 'mk', 'name' => 'Macedonian', 'direction' => 'ltr'),
        'mg'      => array('q' => 1, 'lang' => 'mg', 'name' => 'Malagasy', 'direction' => 'ltr'),
        'ms'      => array('q' => 1, 'lang' => 'ms', 'name' => 'Malay', 'direction' => 'ltr'),
        'ml'      => array('q' => 0.5, 'lang' => 'ml', 'name' => 'Malayalam', 'direction' => 'ltr'),
        'ne'      => array('q' => 1, 'lang' => 'ne', 'name' => 'Nepali', 'direction' => 'ltr'),
        'nb'      => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
        'no'      => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
        'nn'      => array('q' => 1, 'lang' => 'nn', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'),
        'fa'      => array('q' => 1, 'lang' => 'fa', 'name' => 'Persian', 'direction' => 'rtl'),
        'pl'      => array('q' => 0.5, 'lang' => 'pl', 'name' => 'Polish', 'direction' => 'ltr'),
        'pt'      => array('q' => 0.1, 'lang' => 'pt',    'name' => 'Portuguese', 'direction' => 'ltr'),
        'pt-br'   => array('q' => 0.9, 'lang' => 'pt_BR', 'name' => 'Portuguese Brazil', 'direction' => 'ltr'),
        'ru'      => array('q' => 0.9, 'lang' => 'ru', 'name' => 'Russian', 'direction' => 'ltr'),
        'sr-ec'      => array('q' => 1, 'lang' => 'sr-ec', 'name' => 'Serbian', 'direction' => 'ltr'),
        'es'      => array('q' => 1, 'lang' => 'es',    'name' => 'Spanish', 'direction' => 'ltr'),
        'sv'      => array('q' => 0.8, 'lang' => 'sv', 'name' => 'Swedish', 'direction' => 'ltr'),
        'tl'      => array('q' => 0.8, 'lang' => 'tl', 'name' => 'Tagalog', 'direction' => 'ltr'),
        'ta'      => array('q' => 1, 'lang' => 'ta', 'name' => 'Tamil', 'direction' => 'ltr'),
        'te'      => array('q' => 0.3, 'lang' => 'te', 'name' => 'Telugu', 'direction' => 'ltr'),
        'tr'      => array('q' => 0.5, 'lang' => 'tr', 'name' => 'Turkish', 'direction' => 'ltr'),
        'uk'      => array('q' => 1, 'lang' => 'uk', 'name' => 'Ukrainian', 'direction' => 'ltr'),
        'hsb'     => array('q' => 0.8, 'lang' => 'hsb', 'name' => 'Upper Sorbian', 'direction' => 'ltr'),
        'ur_PK'      => array('q' => 1, 'lang' => 'ur', 'name' => 'Urdu (Pakistan)', 'direction' => 'rtl'),
        'vi'      => array('q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'),
    );
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for leaving a group
 *
 * 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>
 * @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/
 */

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

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

/**
 * Form for leaving a group
 *
 * @category Form
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Sarven Capadisli <csarven@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      UnsubscribeForm
 */
class LeaveForm extends Form
{
    /**
     * group for user to leave
     */

    var $group = null;

    /**
     * Constructor
     *
     * @param HTMLOutputter $out   output channel
     * @param group         $group group to leave
     */
    function __construct($out=null, $group=null)
    {
        parent::__construct($out);

        $this->group = $group;
    }

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
        return 'group-leave-' . $this->group->id;
    }

    /**
     * class of the form
     *
     * @return string of the form class
     */
    function formClass()
    {
        return 'form_group_leave ajax';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('leavegroup',
                                array('id' => $this->group->id));
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text on form to leave a group.
        $this->out->submit('submit', _m('BUTTON','Leave'));
    }
}


code:
<?php

/**
 * Based on code from Stomp PHP library, working around bugs in the base class.
 *
 * Original code is copyright 2005-2006 The Apache Software Foundation
 * Modifications copyright 2009 StatusNet Inc by Brion Vibber <brion@status.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class LiberalStomp extends Stomp
{
    /**
     * We need to be able to get the socket so advanced daemons can
     * do a select() waiting for input both from the queue and from
     * other sources such as an XMPP connection.
     *
     * @return resource
     */
    function getSocket()
    {
        return $this->_socket;
    }

    /**
     * Return the host we're currently connected to.
     *
     * @return string
     */
    function getServer()
    {
        $idx = $this->_currentHost;
        if ($idx >= 0) {
            $host = $this->_hosts[$idx];
            return "$host[0]:$host[1]";
        } else {
            return '[unconnected]';
        }
    }

    /**
     * Make socket connection to the server
     * We also set the stream to non-blocking mode, since we'll be
     * select'ing to wait for updates. In blocking mode it seems
     * to get confused sometimes.
     *
     * @throws StompException
     */
    protected function _makeConnection ()
    {
        parent::_makeConnection();
        stream_set_blocking($this->_socket, 0);
    }

    /**
     * Version 1.0.0 of the Stomp library gets confused if messages
     * come in too fast over the connection. This version will read
     * out as many frames as are ready to be read from the socket.
     *
     * Modified from Stomp::readFrame()
     *
     * @return StompFrame False when no frame to read
     */
    public function readFrames ()
    {
        if (!$this->hasFrameToRead()) {
            return false;
        }

        $rb = 1024;
        $data = '';
        $end = false;
        $frames = array();

        do {
            // @fixme this sometimes hangs in blocking mode...
            // shouldn't we have been idle until we found there's more data?
            $read = fread($this->_socket, $rb);
            if ($read === false || ($read === '' && feof($this->_socket))) {
                // @fixme possibly attempt an auto reconnect as old code?
                throw new StompException("Error reading");
                //$this->_reconnect();
                // @fixme this will lose prior items
                //return $this->readFrames();
            }
            $data .= $read;
            if (strpos($data, "\x00") !== false) {
                // Frames are null-delimited, but some servers
                // may append an extra \n according to old bug reports.
                $data = str_replace("\x00\n", "\x00", $data);
                $chunks = explode("\x00", $data);

                $data = array_pop($chunks);
                $frames = array_merge($frames, $chunks);
                if ($data == '') {
                    // We're at the end of a frame; stop reading.
                    break;
                } else {
                    // In the middle of a frame; keep going.
                }
            }
            // @fixme find out why this len < 2 check was there
            //$len = strlen($data);
        } while (true);//$len < 2 || $end == false);

        return array_map(array($this, 'parseFrame'), $frames);
    }

    /**
     * Parse a raw Stomp frame into an object.
     * Extracted from Stomp::readFrame()
     *
     * @param string $data
     * @return StompFrame
     */
    function parseFrame($data)
    {
        list ($header, $body) = explode("\n\n", $data, 2);
        $header = explode("\n", $header);
        $headers = array();
        $command = null;
        foreach ($header as $v) {
            if (isset($command)) {
                list ($name, $value) = explode(':', $v, 2);
                $headers[$name] = $value;
            } else {
                $command = $v;
            }
        }
        $frame = new StompFrame($command, $headers, trim($body));
        if (isset($frame->headers['transformation']) && $frame->headers['transformation'] == 'jms-map-json') {
            require_once 'Stomp/Message/Map.php';
            return new StompMessageMap($frame);
        } else {
            return $frame;
        }
        return $frame;
    }

    /**
     * Write frame to server
     *
     * @param StompFrame $stompFrame
     */
    protected function _writeFrame (StompFrame $stompFrame)
    {
        if (!is_resource($this->_socket)) {
            require_once 'Stomp/Exception.php';
            throw new StompException('Socket connection hasn\'t been established');
        }

        $data = $stompFrame->__toString();

        // Make sure the socket's in a writable state; if not, wait a bit.
        stream_set_blocking($this->_socket, 1);

        $r = fwrite($this->_socket, $data, strlen($data));
        stream_set_blocking($this->_socket, 0);
        if ($r === false || $r == 0) {
            $this->_reconnect();
            $this->_writeFrame($stompFrame);
        }
    }
 }


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Lists a user has created
 *
 * 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    Shashi Gowda <connect2shashi@gmail.com>
 * @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);
}

/**
 * Peopletags a user has subscribed to
 *
 * @category Widget
 * @package  StatusNet
 * @author   Shashi Gowda <connect2shashi@gmail.com>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class ListsNav extends MoreMenu
{
    var $profile=null;
    var $lists=null;

    function __construct($out, Profile $profile)
    {
        parent::__construct($out);
        $this->profile = $profile;

        $user = common_current_user();

        $this->lists = $profile->getLists($user);
    }

    function tag()
    {
        return 'lists';
    }

    function getItems()
    {
        $items = array();

        while ($this->lists->fetch()) {
                $mode = $this->lists->private ? 'private' : 'public';
                $items[] = array('showprofiletag',
                                 array('tagger' => $this->profile->nickname,
                                       'tag'    => $this->lists->tag),
                                 $this->lists->tag,
                                 '');
         }

         return $items;
    }

    function hasLists()
    {
        return (!empty($this->lists) && $this->lists->N > 0);
    }

    function seeAllItem()
    {
        return array('peopletagsbyuser',
                     array('nickname' => $this->profile->nickname),
                     // TRANS: Link description for seeing all lists.
                     _('See all'),
                     // TRANS: Link title for seeing all lists.
                     _('See all lists you have created.'));
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Class for locations
 *
 * 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  Location
 * @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') && !defined('LACONICA')) {
    exit(1);
}

/**
 * class for locations
 *
 * These are stored in the DB as part of notice and profile records,
 * but since they're about the same in both, we have a separate class
 * for them.
 *
 * @category Location
 * @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 Location
{
    public  $lat;
    public  $lon;
    public  $location_id;
    public  $location_ns;
    private $_url;
    private $_rdfurl;

    var $names = array();

    /**
     * Constructor that makes a Location from a string name
     *
     * @param string $name     Human-readable name (any kind)
     * @param string $language Language, default = common_language()
     *
     * @return Location Location with that name (or null if not found)
     */

    static function fromName($name, $language=null)
    {
        if (is_null($language)) {
            $language = common_language();
        }

        $location = null;

        // Let a third-party handle it

        Event::handle('LocationFromName', array($name, $language, &$location));

        return $location;
    }

    /**
     * Constructor that makes a Location from an ID
     *
     * @param integer $id       Identifier ID
     * @param integer $ns       Namespace of the identifier
     * @param string  $language Language to return name in (default is common)
     *
     * @return Location The location with this ID (or null if none)
     */

    static function fromId($id, $ns, $language=null)
    {
        if (is_null($language)) {
            $language = common_language();
        }

        $location = null;

        // Let a third-party handle it

        Event::handle('LocationFromId', array($id, $ns, $language, &$location));

        return $location;
    }

    /**
     * Constructor that finds the nearest location to a lat/lon pair
     *
     * @param float  $lat      Latitude
     * @param float  $lon      Longitude
     * @param string $language Language for results, default = current
     *
     * @return Location the location found, or null if none found
     */

    static function fromLatLon($lat, $lon, $language=null)
    {
        if (is_null($language)) {
            $language = common_language();
        }

        $location = null;

        // Let a third-party handle it

        if (Event::handle('LocationFromLatLon',
                          array($lat, $lon, $language, &$location))) {
            // Default is just the lat/lon pair

            $location = new Location();

            $location->lat = $lat;
            $location->lon = $lon;
        }

        return $location;
    }

    /**
     * Get the name for this location in the given language
     *
     * @param string $language language to use, default = current
     *
     * @return string location name or null if not found
     */

    function getName($language=null)
    {
        if (is_null($language)) {
            $language = common_language();
        }

        if (array_key_exists($language, $this->names)) {
            return $this->names[$language];
        } else {
            $name = null;
            Event::handle('LocationNameLanguage', array($this, $language, &$name));
            if (!empty($name)) {
                $this->names[$language] = $name;
                return $name;
            }
        }
    }

    /**
     * Get an URL suitable for this location
     *
     * @return string URL for this location or NULL
     */

    function getURL()
    {
        // Keep one cached

        if (is_string($this->_url)) {
            return $this->_url;
        }

        $url = null;

        Event::handle('LocationUrl', array($this, &$url));

        $this->_url = $url;

        return $url;
    }

    /**
     * Get an URL for this location, suitable for embedding in RDF
     *
     * @return string URL for this location or NULL
     */

    function getRdfURL()
    {
        // Keep one cached

        if (is_string($this->_rdfurl)) {
            return $this->_rdfurl;
        }

        $url = null;

        Event::handle('LocationRdfUrl', array($this, &$url));

        $this->_rdfurl = $url;

        return $url;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Menu for login 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 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);
}

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

/**
 * Menu for login group of actions
 *
 * @category Output
 * @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 LoginGroupNav extends Menu
{
    /**
     * Show the menu
     *
     * @return void
     */
    function show()
    {
        $action_name = $this->action->trimmed('action');

        $this->action->elementStart('ul', array('class' => 'nav'));

        if (Event::handle('StartLoginGroupNav', array($this->action))) {

            $this->action->menuItem(common_local_url('login'),
                                    // TRANS: Menu item for logging in to the StatusNet site.
                                    _m('MENU','Login'),
                                    // TRANS: Title for menu item for logging in to the StatusNet site.
                                    _('Login with a username and password'),
                                    $action_name === 'login');

            if (!common_logged_in() &&
                !(common_config('site','closed') || common_config('site','inviteonly'))) {
                $this->action->menuItem(common_local_url('register'),
                                        // TRANS: Menu item for registering with the StatusNet site.
                                        _m('MENU','Register'),
                                        // TRANS: Title for menu item for registering with the StatusNet site.
                                        _('Sign up for a new account'),
                                        $action_name === 'register');
            }

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

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


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * utilities for sending email
 *
 * 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  Mail
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Zach Copley <zach@status.net>
 * @author    Robin Millette <millette@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('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once 'Mail.php';

/**
 * return the configured mail backend
 *
 * Uses the $config array to make a mail backend. Cached so it is safe to call
 * more than once.
 *
 * @return Mail backend
 */
function mail_backend()
{
    static $backend = null;

    if (!$backend) {
        $backend = Mail::factory(common_config('mail', 'backend'),
                                 common_config('mail', 'params') ?: array());
        if (PEAR::isError($backend)) {
            common_server_error($backend->getMessage(), 500);
        }
    }
    return $backend;
}

/**
 * send an email to one or more recipients
 *
 * @param array  $recipients array of strings with email addresses of recipients
 * @param array  $headers    array mapping strings to strings for email headers
 * @param string $body       body of the email
 *
 * @return boolean success flag
 */
function mail_send($recipients, $headers, $body)
{
    try {
        // XXX: use Mail_Queue... maybe
        $backend = mail_backend();

        if (!isset($headers['Content-Type'])) {
           $headers['Content-Type'] = 'text/plain; charset=UTF-8';
        }

        assert($backend); // throws an error if it's bad
        $sent = $backend->send($recipients, $headers, $body);
        return true;
    } catch (PEAR_Exception $e) {
        common_log(
            LOG_ERR,
            "Unable to send email - '{$e->getMessage()}'. "
            . 'Is your mail subsystem set up correctly?'
        );
        return false;
    }
}

/**
 * returns the configured mail domain
 *
 * Defaults to the server name.
 *
 * @return string mail domain, suitable for making email addresses.
 */
function mail_domain()
{
    $maildomain = common_config('mail', 'domain');
    if (!$maildomain) {
        $maildomain = common_config('site', 'server');
    }
    return $maildomain;
}

/**
 * returns a good address for sending email from this server
 *
 * Uses either the configured value or a faked-up value made
 * from the mail domain.
 *
 * @return string notify from address
 */
function mail_notify_from()
{
    $notifyfrom = common_config('mail', 'notifyfrom');

    if (!$notifyfrom) {

        $domain = mail_domain();

        $notifyfrom = '"'. str_replace('"', '\\"', common_config('site', 'name')) .'" <noreply@'.$domain.'>';
    }

    return $notifyfrom;
}

/**
 * sends email to a user
 *
 * @param User   &$user   user to send email to
 * @param string $subject subject of the email
 * @param string $body    body of the email
 * @param array  $headers optional list of email headers
 * @param string $address optional specification of email address
 *
 * @return boolean success flag
 */
function mail_to_user(&$user, $subject, $body, $headers=array(), $address=null)
{
    if (!$address) {
        $address = $user->email;
    }

    $recipients = $address;
    $profile    = $user->getProfile();

    $headers['Date']    = date("r", time());
    $headers['From']    = mail_notify_from();
    $headers['To']      = $profile->getBestName() . ' <' . $address . '>';
    $headers['Subject'] = $subject;

    return mail_send($recipients, $headers, $body);
}

/**
 * Send an email to confirm a user's control of an email address
 *
 * @param User   $user     User claiming the email address
 * @param string $code     Confirmation code
 * @param string $nickname Nickname of user
 * @param string $address  email address to confirm
 *
 * @see common_confirmation_code()
 *
 * @return success flag
 */
function mail_confirm_address($user, $code, $nickname, $address, $url=null)
{
    if (empty($url)) {
        $url = common_local_url('confirmaddress', array('code' => $code));
    }

    // TRANS: Subject for address confirmation email.
    $subject = _('Email address confirmation');

    // TRANS: Body for address confirmation email.
    // TRANS: %1$s is the addressed user's nickname, %2$s is the StatusNet sitename,
    // TRANS: %3$s is the URL to confirm at.
    $body = sprintf(_("Hey, %1\$s.\n\n".
                      "Someone just entered this email address on %2\$s.\n\n" .
                      "If it was you, and you want to confirm your entry, ".
                      "use the URL below:\n\n\t%3\$s\n\n" .
                      "If not, just ignore this message.\n\n".
                      "Thanks for your time, \n%2\$s\n"),
                    $nickname,
                    common_config('site', 'name'),
                    $url);

    $headers = array();

    return mail_to_user($user, $subject, $body, $headers, $address);
}

/**
 * notify a user of subscription by another user
 *
 * This is just a wrapper around the profile-based version.
 *
 * @param User $listenee user who is being subscribed to
 * @param User $listener user who is subscribing
 *
 * @see mail_subscribe_notify_profile()
 *
 * @return void
 */
function mail_subscribe_notify($listenee, $listener)
{
    $other = $listener->getProfile();
    mail_subscribe_notify_profile($listenee, $other);
}

/**
 * notify a user of subscription by a profile (remote or local)
 *
 * This function checks to see if the listenee has an email
 * address and wants subscription notices.
 *
 * @param User    $listenee user who's being subscribed to
 * @param Profile $other    profile of person who's listening
 *
 * @return void
 */
function mail_subscribe_notify_profile($listenee, $other)
{
    if ($other->hasRight(Right::EMAILONSUBSCRIBE) &&
        $listenee->email && $listenee->emailnotifysub) {

        $profile = $listenee->getProfile();

        $name = $profile->getBestName();

        $long_name = ($other->fullname) ?
          ($other->fullname . ' (' . $other->nickname . ')') : $other->nickname;

        $recipients = $listenee->email;

        // use the recipient's localization
        common_switch_locale($listenee->language);

        $headers = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname);
        $headers['From']    = mail_notify_from();
        $headers['To']      = $name . ' <' . $listenee->email . '>';
        // TRANS: Subject of new-subscriber notification e-mail.
        // TRANS: %1$s is the subscribing user's nickname, %2$s is the StatusNet sitename.
        $headers['Subject'] = sprintf(_('%1$s is now following you on %2$s.'),
                                      $other->getBestName(),
                                      common_config('site', 'name'));

        // TRANS: Main body of new-subscriber notification e-mail.
        // TRANS: %1$s is the subscriber's long name, %2$s is the StatusNet sitename.
        $body = sprintf(_('%1$s is now following you on %2$s.'),
                        $long_name,
                        common_config('site', 'name')) .
                mail_profile_block($other) .
                mail_footer_block();

        // reset localization
        common_switch_locale();
        mail_send($recipients, $headers, $body);
    }
}

function mail_subscribe_pending_notify_profile($listenee, $other)
{
    if ($other->hasRight(Right::EMAILONSUBSCRIBE) &&
        $listenee->email && $listenee->emailnotifysub) {

        $profile = $listenee->getProfile();

        $name = $profile->getBestName();

        $long_name = ($other->fullname) ?
          ($other->fullname . ' (' . $other->nickname . ')') : $other->nickname;

        $recipients = $listenee->email;

        // use the recipient's localization
        common_switch_locale($listenee->language);

        $headers = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname);
        $headers['From']    = mail_notify_from();
        $headers['To']      = $name . ' <' . $listenee->email . '>';
        // TRANS: Subject of pending new-subscriber notification e-mail.
        // TRANS: %1$s is the subscribing user's nickname, %2$s is the StatusNet sitename.
        $headers['Subject'] = sprintf(_('%1$s would like to listen to '.
                                        'your notices on %2$s.'),
                                      $other->getBestName(),
                                      common_config('site', 'name'));

        // TRANS: Main body of pending new-subscriber notification e-mail.
        // TRANS: %1$s is the subscriber's long name, %2$s is the StatusNet sitename.
        $body = sprintf(_('%1$s would like to listen to your notices on %2$s. ' .
                          'You may approve or reject their subscription at %3$s'),
                        $long_name,
                        common_config('site', 'name'),
                        common_local_url('subqueue', array('nickname' => $listenee->nickname))) .
                mail_profile_block($other) .
                mail_footer_block();

        // reset localization
        common_switch_locale();
        mail_send($recipients, $headers, $body);
    }
}

function mail_footer_block()
{
    // TRANS: Common footer block for StatusNet notification emails.
    // TRANS: %1$s is the StatusNet sitename,
    // TRANS: %2$s is a link to the addressed user's e-mail settings.
    return "\n\n" . sprintf(_('Faithfully yours,'.
                              "\n".'%1$s.'."\n\n".
                              "----\n".
                              "Change your email address or ".
                              "notification options at ".'%2$s'),
                            common_config('site', 'name'),
                            common_local_url('emailsettings')) . "\n";
}

/**
 * Format a block of profile info for a plaintext notification email.
 *
 * @param Profile $profile
 * @return string
 */
function mail_profile_block($profile)
{
    // TRANS: Layout for
    // TRANS: %1$s is the subscriber's profile URL, %2$s is the subscriber's location (or empty)
    // TRANS: %3$s is the subscriber's homepage URL (or empty), %4%s is the subscriber's bio (or empty)
    $out = array();
    $out[] = "";
    $out[] = "";
    // TRANS: Profile info line in notification e-mail.
    // TRANS: %s is a URL.
    $out[] = sprintf(_("Profile: %s"), $profile->profileurl);
    if ($profile->location) {
        // TRANS: Profile info line in notification e-mail.
        // TRANS: %s is a location.
        $out[] = sprintf(_("Location: %s"), $profile->location);
    }
    if ($profile->homepage) {
        // TRANS: Profile info line in notification e-mail.
        // TRANS: %s is a homepage.
        $out[] = sprintf(_("Homepage: %s"), $profile->homepage);
    }
    if ($profile->bio) {
        // TRANS: Profile info line in notification e-mail.
        // TRANS: %s is biographical information.
        $out[] = sprintf(_("Bio: %s"), $profile->bio);
    }

    $blocklink = common_local_url('block', array('profileid' => $profile->id));
    // This'll let ModPlus add the remote profile info so it's possible
    // to block remote users directly...
    Event::handle('MailProfileInfoBlockLink', array($profile, &$blocklink));

    // TRANS: This is a paragraph in a new-subscriber e-mail.
    // TRANS: %s is a URL where the subscriber can be reported as abusive.
    $out[] = sprintf(_('If you believe this account is being used abusively, ' .
                       'you can block them from your subscribers list and ' .
                       'report as spam to site administrators at %s.'),
                     $blocklink);
    $out[] = "";

    return implode("\n", $out);
}

/**
 * notify a user of their new incoming email address
 *
 * User's email and incoming fields should already be updated.
 *
 * @param User $user user with the new address
 *
 * @return void
 */
function mail_new_incoming_notify($user)
{
    $profile = $user->getProfile();

    $name = $profile->getBestName();

    $headers['From']    = $user->incomingemail;
    $headers['To']      = $name . ' <' . $user->email . '>';
    // TRANS: Subject of notification mail for new posting email address.
    // TRANS: %s is the StatusNet sitename.
    $headers['Subject'] = sprintf(_('New email address for posting to %s'),
                                  common_config('site', 'name'));

    // TRANS: Body of notification mail for new posting email address.
    // TRANS: %1$s is the StatusNet sitename, %2$s is the e-mail address to send
    // TRANS: to to post by e-mail, %3$s is a URL to more instructions.
    $body = sprintf(_("You have a new posting address on %1\$s.\n\n".
                      "Send email to %2\$s to post new messages.\n\n".
                      "More email instructions at %3\$s."),
                    common_config('site', 'name'),
                    $user->incomingemail,
                    common_local_url('doc', array('title' => 'email'))) .
            mail_footer_block();

    mail_send($user->email, $headers, $body);
}

/**
 * generate a new address for incoming messages
 *
 * @todo check the database for uniqueness
 *
 * @return string new email address for incoming messages
 */
function mail_new_incoming_address()
{
    $prefix = common_confirmation_code(64);
    $suffix = mail_domain();
    return $prefix . '@' . $suffix;
}

/**
 * broadcast a notice to all subscribers with SMS notification on
 *
 * This function sends SMS messages to all users who have sms addresses;
 * have sms notification on; and have sms enabled for this particular
 * subscription.
 *
 * @param Notice $notice The notice to broadcast
 *
 * @return success flag
 */
function mail_broadcast_notice_sms($notice)
{
    // Now, get users subscribed to this profile

    $user = new User();

    $UT = common_config('db','type')=='pgsql'?'"user"':'user';
    $replies = $notice->getReplies();
    $user->query('SELECT nickname, smsemail, incomingemail ' .
                 "FROM $UT LEFT OUTER JOIN subscription " .
                 "ON $UT.id = subscription.subscriber " .
                 'AND subscription.subscribed = ' . $notice->profile_id . ' ' .
                 'AND subscription.subscribed != subscription.subscriber ' .
                 // Users (other than the sender) who `want SMS notices':
                 "WHERE $UT.id != " . $notice->profile_id . ' ' .
                 "AND $UT.smsemail IS NOT null " .
                 "AND $UT.smsnotify = 1 " .
                 // ... where either the user _is_ subscribed to the sender
                 // (any of the "subscription" fields IS NOT null)
                 // and wants to get SMS for all of this scribe's notices...
                 'AND (subscription.sms = 1 ' .
                 // ... or where the user was mentioned in
                 // or replied-to with the notice:
                 ($replies ? sprintf("OR $UT.id in (%s)",
                                     implode(',', $replies))
                           : '') .
                 ')');

    while ($user->fetch()) {
        common_log(LOG_INFO,
                   'Sending notice ' . $notice->id . ' to ' . $user->smsemail,
                   __FILE__);
        $success = mail_send_sms_notice_address($notice,
                                                $user->smsemail,
                                                $user->incomingemail,
                                                $user->nickname);
        if (!$success) {
            // XXX: Not sure, but I think that's the right thing to do
            common_log(LOG_WARNING,
                       'Sending notice ' . $notice->id . ' to ' .
                       $user->smsemail . ' FAILED, cancelling.',
                       __FILE__);
            return false;
        }
    }

    $user->free();
    unset($user);

    return true;
}

/**
 * send a notice to a user via SMS
 *
 * A convenience wrapper around mail_send_sms_notice_address()
 *
 * @param Notice $notice notice to send
 * @param User   $user   user to receive notice
 *
 * @see mail_send_sms_notice_address()
 *
 * @return boolean success flag
 */
function mail_send_sms_notice($notice, $user)
{
    return mail_send_sms_notice_address($notice,
                                        $user->smsemail,
                                        $user->incomingemail,
                                        $user->nickname);
}

/**
 * send a notice to an SMS email address from a given address
 *
 * We use the user's incoming email address as the "From" address to make
 * replying to notices easier.
 *
 * @param Notice $notice        notice to send
 * @param string $smsemail      email address to send to
 * @param string $incomingemail email address to set as 'from'
 * @param string $nickname      nickname to add to beginning
 *
 * @return boolean success flag
 */
function mail_send_sms_notice_address($notice, $smsemail, $incomingemail, $nickname)
{
    $to = $nickname . ' <' . $smsemail . '>';

    $other = $notice->getProfile();

    common_log(LOG_INFO, 'Sending notice ' . $notice->id .
               ' to ' . $smsemail, __FILE__);

    $headers = array();

    $headers['From']    = ($incomingemail) ? $incomingemail : mail_notify_from();
    $headers['To']      = $to;
    // TRANS: Subject line for SMS-by-email notification messages.
    // TRANS: %s is the posting user's nickname.
    $headers['Subject'] = sprintf(_('%s status'),
                                  $other->getBestName());

    $body = $notice->content;

    return mail_send($smsemail, $headers, $body);
}

/**
 * send a message to confirm a claim for an SMS number
 *
 * @param string $code     confirmation code
 * @param string $nickname nickname of user claiming number
 * @param string $address  email address to send the confirmation to
 *
 * @see common_confirmation_code()
 *
 * @return void
 */
function mail_confirm_sms($code, $nickname, $address)
{
    $recipients = $address;

    $headers['From']    = mail_notify_from();
    $headers['To']      = $nickname . ' <' . $address . '>';
    // TRANS: Subject line for SMS-by-email address confirmation message.
    $headers['Subject'] = _('SMS confirmation');

    // TRANS: Main body heading for SMS-by-email address confirmation message.
    // TRANS: %s is the addressed user's nickname.
    $body  = sprintf(_('%s: confirm you own this phone number with this code:'), $nickname);
    $body .= "\n\n";
    $body .= $code;
    $body .= "\n\n";

    mail_send($recipients, $headers, $body);
}

/**
 * send a mail message to notify a user of a 'nudge'
 *
 * @param User $from user nudging
 * @param User $to   user being nudged
 *
 * @return boolean success flag
 */
function mail_notify_nudge($from, $to)
{
    common_switch_locale($to->language);
    // TRANS: Subject for 'nudge' notification email.
    // TRANS: %s is the nudging user.
    $subject = sprintf(_('You have been nudged by %s'), $from->nickname);

    $from_profile = $from->getProfile();

    // TRANS: Body for 'nudge' notification email.
    // TRANS: %1$s is the nuding user's long name, $2$s is the nudging user's nickname,
    // TRANS: %3$s is a URL to post notices at.
    $body = sprintf(_("%1\$s (%2\$s) is wondering what you are up to ".
                      "these days and is inviting you to post some news.\n\n".
                      "So let's hear from you :)\n\n".
                      "%3\$s\n\n".
                      "Don't reply to this email; it won't get to them."),
                    $from_profile->getBestName(),
                    $from->nickname,
                    common_local_url('all', array('nickname' => $to->nickname))) .
            mail_footer_block();
    common_switch_locale();

    $headers = _mail_prepare_headers('nudge', $to->nickname, $from->nickname);

    return mail_to_user($to, $subject, $body, $headers);
}

/**
 * send a message to notify a user of a direct message (DM)
 *
 * This function checks to see if the recipient wants notification
 * of DMs and has a configured email address.
 *
 * @param Message $message message to notify about
 * @param User    $from    user sending message; default to sender
 * @param User    $to      user receiving message; default to recipient
 *
 * @return boolean success code
 */
function mail_notify_message($message, $from=null, $to=null)
{
    if (is_null($from)) {
        $from = User::getKV('id', $message->from_profile);
    }

    if (is_null($to)) {
        $to = User::getKV('id', $message->to_profile);
    }

    if (is_null($to->email) || !$to->emailnotifymsg) {
        return true;
    }

    common_switch_locale($to->language);
    // TRANS: Subject for direct-message notification email.
    // TRANS: %s is the sending user's nickname.
    $subject = sprintf(_('New private message from %s'), $from->nickname);

    $from_profile = $from->getProfile();

    // TRANS: Body for direct-message notification email.
    // TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname,
    // TRANS: %3$s is the message content, %4$s a URL to the message,
    $body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n".
                      "------------------------------------------------------\n".
                      "%3\$s\n".
                      "------------------------------------------------------\n\n".
                      "You can reply to their message here:\n\n".
                      "%4\$s\n\n".
                      "Don't reply to this email; it won't get to them."),
                    $from_profile->getBestName(),
                    $from->nickname,
                    $message->content,
                    common_local_url('newmessage', array('to' => $from->id))) .
            mail_footer_block();

    $headers = _mail_prepare_headers('message', $to->nickname, $from->nickname);

    common_switch_locale();
    return mail_to_user($to, $subject, $body, $headers);
}

/**
 * Notify a user that they have received an "attn:" message AKA "@-reply"
 *
 * @param User   $user   The user who recevied the notice
 * @param Notice $notice The notice that was sent
 *
 * @return void
 */
function mail_notify_attn($user, $notice)
{
    if (!$user->receivesEmailNotifications()) {
        return;
    }

    $sender = $notice->getProfile();

    if ($sender->id == $user->id) {
        return;
    }

    // See if the notice's author who mentions this user is sandboxed
    if (!$sender->hasRight(Right::EMAILONREPLY)) {
        return;
    }

    // If the author has blocked the author, don't spam them with a notification.
    if ($user->hasBlocked($sender)) {
        return;
    }

    $bestname = $sender->getBestName();

    common_switch_locale($user->language);

    if ($notice->hasConversation()) {
        $conversationUrl = common_local_url('conversation',
                         array('id' => $notice->conversation)).'#notice-'.$notice->id;
        // TRANS: Line in @-reply notification e-mail. %s is conversation URL.
        $conversationEmailText = sprintf(_("The full conversation can be read here:\n\n".
                                           "\t%s"), $conversationUrl) . "\n\n";
    } else {
        $conversationEmailText = '';
    }

    // TRANS: E-mail subject for notice notification.
    // TRANS: %1$s is the sending user's long name, %2$s is the adding user's nickname.
    $subject = sprintf(_('%1$s (@%2$s) sent a notice to your attention'), $bestname, $sender->nickname);

        // TRANS: Body of @-reply notification e-mail.
        // TRANS: %1$s is the sending user's name, $2$s is the StatusNet sitename,
        // TRANS: %3$s is a URL to the notice, %4$s is the notice text,
        // TRANS: %5$s is the text "The full conversation can be read here:" and a URL to the full conversion if it exists (otherwise empty),
        // TRANS: %6$s is a URL to reply to the notice, %7$s is a URL to all @-replies for the addressed user,
        $body = sprintf(_("%1\$s just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
                      "The notice is here:\n\n".
                      "\t%3\$s\n\n" .
                      "It reads:\n\n".
                      "\t%4\$s\n\n" .
                      "%5\$s" .
                      "You can reply back here:\n\n".
                      "\t%6\$s\n\n" .
                      "The list of all @-replies for you here:\n\n" .
                      "%7\$s"),
                    $sender->getFancyName(),//%1
                    common_config('site', 'name'),//%2
                    common_local_url('shownotice',
                                     array('notice' => $notice->id)),//%3
                    $notice->content,//%4
                    $conversationEmailText,//%5
                    common_local_url('newnotice',
                                     array('replyto' => $sender->nickname, 'inreplyto' => $notice->id)),//%6
                    common_local_url('replies',
                                     array('nickname' => $user->nickname))) . //%7
                mail_footer_block();
    $headers = _mail_prepare_headers('mention', $user->nickname, $sender->nickname);

    common_switch_locale();
    mail_to_user($user, $subject, $body, $headers);
}

/**
 * Prepare the common mail headers used in notification emails
 *
 * @param string $msg_type type of message being sent to the user
 * @param string $to       nickname of the receipient
 * @param string $from     nickname of the user triggering the notification
 *
 * @return array list of mail headers to include in the message
 */
function _mail_prepare_headers($msg_type, $to, $from)
{
    $headers = array(
        'X-StatusNet-MessageType' => $msg_type,
        'X-StatusNet-TargetUser'  => $to,
        'X-StatusNet-SourceUser'  => $from,
        'X-StatusNet-Domain'      => common_config('site', 'server')
    );

    return $headers;
}

/**
 * Send notification emails to group administrator.
 *
 * @param User_group $group
 * @param Profile $joiner
 */
function mail_notify_group_join($group, $joiner)
{
    // This returns a Profile query...
    $admin = $group->getAdmins();
    while ($admin->fetch()) {
        // We need a local user for email notifications...
        $adminUser = User::getKV('id', $admin->id);
        // @fixme check for email preference?
        if ($adminUser && $adminUser->email) {
            // use the recipient's localization
            common_switch_locale($adminUser->language);

            $headers = _mail_prepare_headers('join', $admin->nickname, $joiner->nickname);
            $headers['From']    = mail_notify_from();
            $headers['To']      = $admin->getBestName() . ' <' . $adminUser->email . '>';
            // TRANS: Subject of group join notification e-mail.
            // TRANS: %1$s is the joining user's nickname, %2$s is the group name, and %3$s is the StatusNet sitename.
            $headers['Subject'] = sprintf(_('%1$s has joined '.
                                            'your group %2$s on %3$s'),
                                          $joiner->getBestName(),
                                          $group->getBestName(),
                                          common_config('site', 'name'));

            // TRANS: Main body of group join notification e-mail.
            // TRANS: %1$s is the subscriber's long name, %2$s is the group name, and %3$s is the StatusNet sitename,
            // TRANS: %4$s is a block of profile info about the subscriber.
            // TRANS: %5$s is a link to the addressed user's e-mail settings.
            $body = sprintf(_('%1$s has joined your group %2$s on %3$s.'),
                            $joiner->getFancyName(),
                            $group->getFancyName(),
                            common_config('site', 'name')) .
                    mail_profile_block($joiner) .
                    mail_footer_block();

            // reset localization
            common_switch_locale();
            mail_send($adminUser->email, $headers, $body);
        }
    }
}

/**
 * Send notification emails to group administrator.
 *
 * @param User_group $group
 * @param Profile $joiner
 */
function mail_notify_group_join_pending($group, $joiner)
{
    $admin = $group->getAdmins();
    while ($admin->fetch()) {
        // We need a local user for email notifications...
        $adminUser = User::getKV('id', $admin->id);
        // @fixme check for email preference?
        if ($adminUser && $adminUser->email) {
            // use the recipient's localization
            common_switch_locale($adminUser->language);

            $headers = _mail_prepare_headers('join', $admin->nickname, $joiner->nickname);
            $headers['From']    = mail_notify_from();
            $headers['To']      = $admin->getBestName() . ' <' . $adminUser->email . '>';
            // TRANS: Subject of pending group join request notification e-mail.
            // TRANS: %1$s is the joining user's nickname, %2$s is the group name, and %3$s is the StatusNet sitename.
            $headers['Subject'] = sprintf(_('%1$s wants to join your group %2$s on %3$s.'),
                                          $joiner->getBestName(),
                                          $group->getBestName(),
                                          common_config('site', 'name'));

            // TRANS: Main body of pending group join request notification e-mail.
            // TRANS: %1$s is the subscriber's long name, %2$s is the group name, and %3$s is the StatusNet sitename,
            // TRANS: %4$s is the URL to the moderation queue page.
            $body = sprintf(_('%1$s would like to join your group %2$s on %3$s. ' .
                              'You may approve or reject their group membership at %4$s'),
                            $joiner->getFancyName(),
                            $group->getFancyName(),
                            common_config('site', 'name'),
                            common_local_url('groupqueue', array('nickname' => $group->nickname))) .
                    mail_profile_block($joiner) .
                    mail_footer_block();

            // reset localization
            common_switch_locale();
            mail_send($adminUser->email, $headers, $body);
        }
    }
}