code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Widget to show a mini-list of groups
 *
 * 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  Public
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

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

define('GROUPS_PER_MINILIST', 8);

/**
 * Widget to show a list of groups, good for sidebar
 *
 * @category Public
 * @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 GroupMiniList extends GroupList
{
    function show()
    {
        $this->out->elementStart('ul', 'entities groups xoxo');

        $cnt = 0;

        while ($this->group->fetch()) {
            $cnt++;
            if($cnt > GROUPS_PER_MINILIST) {
                break;
            }
            $this->showGroup();
        }

        $this->out->elementEnd('ul');

        return $cnt;
    }

    function showGroup()
    {
        $this->out->elementStart('li', 'h-card');
        $this->out->elementStart('a', array('title' => $this->group->getBestName(),
                                       'href' => $this->group->homeUrl(),
                                       'rel' => 'contact group',
                                       'class' => 'p-name u-url org'));

        $logo = ($this->group->mini_logo) ?
          $this->group->mini_logo : User_group::defaultLogo(AVATAR_MINI_SIZE);

        $this->out->element('img', array('src' => $logo,
                                    'width' => AVATAR_MINI_SIZE,
                                    'height' => AVATAR_MINI_SIZE,
                                    'class' => 'avatar photo',
                                    'alt' => $this->group->getBestName()));
        $this->out->elementEnd('a');
        $this->out->elementEnd('li');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Class for an exception when the group profile is missing
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Exception
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
 * @link      http://status.net/
 */

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

/**
 * Class for an exception when the group profile is missing
 *
 * @category Exception
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
 * @link     http://status.net/
 */

class GroupNoProfileException extends NoProfileException
{
    protected $group = null;

    /**
     * constructor
     *
     * @param User_group $user User_group that's missing a profile
     */

    public function __construct(User_group $group)
    {
        $this->group = $group;

        // TRANS: Exception text shown when no profile can be found for a group.
        // TRANS: %1$s is a group nickname, $2$d is a group profile_id (number).
        $message = sprintf(_('User %1$s (%2$d) has no profile record.'),
                           $group->nickname, $group->id);

        parent::__construct($group->profile_id, $message);
    }

    /**
     * Accessor for user
     *
     * @return User_group the group that triggered this exception
     */

    public function getGroup()
    {
        return $this->group;
    }
}

license: mit


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Stream of notices for a group
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

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

/**
 * Stream of notices for a group
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class GroupNoticeStream extends ScopingNoticeStream
{
    var $group;
    var $userProfile;

    function __construct($group, $profile = -1)
    {
        if (is_int($profile) && $profile == -1) {
            $profile = Profile::current();
        }
        $this->group = $group;
        $this->userProfile = $profile;

        parent::__construct(new CachingNoticeStream(new RawGroupNoticeStream($group),
                                                    'user_group:notice_ids:' . $group->id),
                            $profile);
    }

    function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        if ($this->impossibleStream()) {
            return array();
        } else {
            return parent::getNoticeIds($offset, $limit, $since_id, $max_id);
        }
    }

    function getNotices($offset, $limit, $sinceId = null, $maxId = null)
    {
        if ($this->impossibleStream()) {
            return new ArrayWrapper(array());
        } else {
            return parent::getNotices($offset, $limit, $sinceId, $maxId);
        }
    }

    function impossibleStream()
    {
        if ($this->group->force_scope &&
            (empty($this->userProfile) || !$this->userProfile->isMember($this->group))) {
            return true;
        }

        return false;
    }
}

/**
 * Stream of notices for a group
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class RawGroupNoticeStream extends NoticeStream
{
    protected $group;

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

    function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        $inbox = new Group_inbox();

        $inbox->group_id = $this->group->id;

        $inbox->selectAdd();
        $inbox->selectAdd('notice_id');

        Notice::addWhereSinceId($inbox, $since_id, 'notice_id');
        Notice::addWhereMaxId($inbox, $max_id, 'notice_id');

        $inbox->orderBy('created DESC, notice_id DESC');

        if (!is_null($offset)) {
            $inbox->limit($offset, $limit);
        }

        $ids = array();

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

        return $ids;
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Profile block to show for a group
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

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

/**
 * Profile block to show for a group
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class GroupProfileBlock extends ProfileBlock
{
    protected $group = null;

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

    protected function showAvatar(Profile $profile, $size=null)
    {
        $avatar_url = $profile->getGroup()->homepage_logo ?: User_group::defaultLogo($size ?: $this->avatarSize());
        $this->out->element('img', array('src' => $avatar_url,
                                         'class' => 'avatar u-photo',
                                         'width' => $this->avatarSize(),
                                         'height' => $this->avatarSize(),
                                         'alt' => $profile->getBestName()));
    }

    function name()
    {
        return $this->group->getBestName();
    }

    function url()
    {
        return $this->group->homeUrl();
    }

    function location()
    {
        return $this->group->location;
    }

    function homepage()
    {
        return $this->group->homepage;
    }

    function description()
    {
        return $this->group->description;
    }

    function otherProfiles()
    {
        return array();
    }

    function showActions()
    {
        $cur = common_current_user();
        $this->out->elementStart('div', 'entity_actions');
        // TRANS: Group actions header (h2). Text hidden by default.
        $this->out->element('h2', null, _('Group actions'));
        $this->out->elementStart('ul');
        if (Event::handle('StartGroupActionsList', array($this, $this->group))) {
            $this->out->elementStart('li', 'entity_subscribe');
            if (Event::handle('StartGroupSubscribe', array($this, $this->group))) {
                if ($cur) {
                    $profile = $cur->getProfile();
                    if ($profile->isMember($this->group)) {
                        $lf = new LeaveForm($this->out, $this->group);
                        $lf->show();
                    } else if ($profile->isPendingMember($this->group)) {
                        $cf = new CancelGroupForm($this->out, $this->group);
                        $cf->show();
                    } else if (!Group_block::isBlocked($this->group, $profile)) {
                        $jf = new JoinForm($this->out, $this->group);
                        $jf->show();
                    }
                }
                Event::handle('EndGroupSubscribe', array($this, $this->group));
            }
            $this->out->elementEnd('li');
            if ($cur && $cur->isAdmin($this->group)) {
                $this->out->elementStart('li', 'entity_edit');
                $this->out->element('a', array('href' => common_local_url('editgroup',
                                                                          array('nickname' => $this->group->nickname)),
                                               // TRANS: Tooltip for menu item in the group navigation page. Only shown for group administrators.
                                               // TRANS: %s is the nickname of the group.
                                               'title' => sprintf(_m('TOOLTIP','Edit %s group properties'), $this->group->nickname)),
                                    // TRANS: Link text for link on user profile.
                                    _m('BUTTON','Edit'));
                $this->out->elementEnd('li');
                $this->out->elementStart('li', 'entity_edit');
                $this->out->element('a', array('href' => common_local_url('grouplogo',
                                                                          array('nickname' => $this->group->nickname)),
                                               // TRANS: Tooltip for menu item in the group navigation page. Only shown for group administrators.
                                               // TRANS: %s is the nickname of the group.
                                               'title' => sprintf(_m('TOOLTIP','Add or edit %s logo'), $this->group->nickname)),
                                    // TRANS: Link text for link on user profile.
                                    _m('MENU','Logo'));
                $this->out->elementEnd('li');
            }
            if ($cur && $cur->hasRight(Right::DELETEGROUP)) {
                $this->out->elementStart('li', 'entity_delete');
                $df = new DeleteGroupForm($this->out, $this->group);
                $df->show();
                $this->out->elementEnd('li');
            }

            Event::handle('EndGroupActionsList', array($this, $this->group));
        }
        $this->out->elementEnd('ul');
        $this->out->elementEnd('div');
    }

    function show()
    {
        $this->out->elementStart('div', 'profile_block group_profile_block section');
        if (Event::handle('StartShowGroupProfileBlock', array($this->out, $this->group))) {
            parent::show();
            Event::handle('EndShowGroupProfileBlock', array($this->out, $this->group));
        }
        $this->out->elementEnd('div');
    }

    function showName()
    {
        parent::showName();
        $this->showAliases();
    }

    function showAliases()
    {
        $aliases = $this->group->getAliases();

        if (!empty($aliases)) {
            $this->out->elementStart('ul', 'group_aliases');
            foreach ($aliases as $alias) {
                $this->out->element('li', 'group_alias', $alias);
            }
            $this->out->elementEnd('ul');
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Groups with the most members section
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * Groups with the most members section
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GroupsByMembersSection extends GroupSection
{
    function getGroups()
    {
        $qry = 'SELECT user_group.*, count(*) as value ' .
          'FROM user_group JOIN group_member '.
          'ON user_group.id = group_member.group_id ' .
          'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified ' .
          'ORDER BY value DESC ';

        $limit = GROUPS_PER_SECTION;
        $offset = 0;

        if (common_config('db','type') == 'pgsql') {
            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
        } else {
            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
        }

        $group = Memcached_DataObject::cachedQuery('User_group',
                                                   $qry,
                                                   3600);
        return $group;
    }

    function title()
    {
        // TRANS: Title for groups with the most members section.
        return _('Popular groups');
    }

    function divId()
    {
        return 'top_groups_by_member';
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Groups with the most posts section
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * Groups with the most posts section
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GroupsByPostsSection extends GroupSection
{
    function getGroups()
    {
        $qry = 'SELECT user_group.*, count(*) as value ' .
          'FROM user_group JOIN group_inbox '.
          'ON user_group.id = group_inbox.group_id ' .
          'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified ' .
          'ORDER BY value DESC ';

        $limit = GROUPS_PER_SECTION;
        $offset = 0;

        if (common_config('db','type') == 'pgsql') {
            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
        } else {
            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
        }

        $group = Memcached_DataObject::cachedQuery('User_group',
                                                   $qry,
                                                   3600);
        return $group;
    }

    function title()
    {
        // TRANS: Title for groups with the most posts section.
        return _('Active groups');
    }

    function divId()
    {
        return 'top_groups_by_post';
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Base class for sections showing lists of groups
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

define('GROUPS_PER_SECTION', 6);

/**
 * Base class for sections
 *
 * These are the widgets that show interesting data about a person
 * group, or site.
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
abstract class GroupSection extends Section
{
    function showContent()
    {
        $profiles = $this->getGroups();

        if (!$profiles) {
            return false;
        }

        $cnt = 0;

        $this->out->elementStart('table');

        while ($profiles->fetch() && ++$cnt <= GROUPS_PER_SECTION) {
            $this->showGroup($profiles);
        }

        $this->out->elementEnd('table');

        return ($cnt > GROUPS_PER_SECTION);
    }

    function getGroups()
    {
        return null;
    }

    function showGroup($group)
    {
        $this->out->elementStart('tr');
        $this->out->elementStart('td');
        $this->out->elementStart('a', array('title' => $group->getBestName(),
                                            'href' => $group->homeUrl(),
                                            'rel' => 'contact group',
                                            'class' => 'h-card org nickname'));
        $logo = $group->stream_logo ?: User_group::defaultLogo(AVATAR_STREAM_SIZE);
        $this->out->element('img', array('src' => $logo,
                                         'width' => AVATAR_MINI_SIZE,
                                         'height' => AVATAR_MINI_SIZE,
                                         'class' => 'avatar u-photo',
                                         'alt' =>  $group->getBestName()));
        $this->out->text($group->nickname);
        $this->out->elementEnd('a');
        $this->out->elementEnd('td');
        if ($group->value) {
            $this->out->element('td', 'value', $group->value);
        }
        $this->out->elementEnd('tr');
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Menu for streams
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Cache
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

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

/**
 * Menu for streams you follow
 *
 * @category  General
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class GroupsNav extends MoreMenu
{
    protected $user;
    protected $groups;

    function __construct($action, $user)
    {
        parent::__construct($action);
        $this->user = $user;
        $this->groups = $user->getGroups();
    }

    function haveGroups()
    {
        return ($this->groups instanceof User_group && $this->groups->N > 0);
    }

    function tag()
    {
        return 'groups';
    }

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

        while ($this->groups instanceof User_group && $this->groups->fetch()) {
            $items[] = array('placeholder',
                             array('nickname' => $this->groups->nickname,
                                   'mainpage' => $this->groups->homeUrl()),
                             $this->groups->getBestName(),
                             $this->groups->getBestName()
                            );
        }

        return $items;
    }

    function seeAllItem() {
        return array('usergroups',
                     array('nickname' => $this->user->nickname),
                     // TRANS: Link description for seeing all groups.
                     _('See all'),
                     // TRANS: Link title for seeing all groups.
                     _('See all groups you belong to.'));
    }

    function item($actionName, $args, $label, $description, $id=null, $cls=null)
    {
        if ($actionName != 'placeholder') {
            return parent::item($actionName, $args, $label, $description, $id, $cls);
        }

        if (empty($id)) {
            $id = $this->menuItemID('showgroup', array('nickname' => $args['nickname']));
        }

        $url = $args['mainpage'];

        $this->out->menuItem($url,
                             $label,
                             $description,
                             $this->isCurrent($actionName, $args),
                             $id,
                             $cls);
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Personal tag cloud section
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Widget
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * Group tag cloud section
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GroupTagCloudSection extends TagCloudSection
{
    var $group = null;

    function __construct($out=null, $group=null)
    {
        parent::__construct($out);
        $this->group = $group;
    }

    function title()
    {
        // TRANS: Title for group tag cloud section.
        // TRANS: %s is a group name.
        return _('Tags');
    }

    function getTags()
    {
        $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff'));
        // @fixme should we use the cutoff too? Doesn't help with indexing per-group.

        $names = $this->group->getAliases();

        $names = array_merge(array($this->group->nickname), $names);

        // XXX This is dumb.

        $quoted = array();

        foreach ($names as $name) {
            $quoted[] = "'$name'";
        }

        $namestring = implode(',', $quoted);

        $qry = 'SELECT notice_tag.tag, '.
          $weightexpr . ' as weight ' .
          'FROM notice_tag JOIN notice ' .
          'ON notice_tag.notice_id = notice.id ' .
          'JOIN group_inbox on group_inbox.notice_id = notice.id ' .
          'WHERE group_inbox.group_id = %d ' .
          'AND notice_tag.tag not in (%s) '.
          'GROUP BY notice_tag.tag ' .
          'ORDER BY weight DESC ';

        $limit = TAGS_PER_SECTION;
        $offset = 0;

        if (common_config('db','type') == 'pgsql') {
            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
        } else {
            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
        }

        $tag = Memcached_DataObject::cachedQuery('Notice_tag',
                                                 sprintf($qry,
                                                         $this->group->id,
                                                         $namestring),
                                                 3600);
        return $tag;
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Stub sub-menu for sections that need to go home
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Menu
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

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

/**
 * stub sub-menu for settings and stuff
 *
 * @category  General
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class HomeStubNav extends Menu
{
    function getItems()
    {
        return array(array('top',
                           array(),
                           // TRANS: Menu item in personal group navigation menu.
                           _m('MENU','Home'),
                           // TRANS: Menu item title in personal group navigation menu.
                           // TRANS: %s is a username.
                           _('Back to top'),
                           'nav_return_top'));
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Low-level generator for HTML
 *
 * 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  Output
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2008 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

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

// Can include XHTML options but these are too fragile in practice.
define('PAGE_TYPE_PREFS', 'text/html');

/**
 * Low-level generator for HTML
 *
 * Abstracts some of the code necessary for HTML generation. Especially
 * has methods for generating HTML form elements. Note that these have
 * been created kind of haphazardly, not with an eye to making a general
 * HTML-creation class.
 *
 * @category Output
 * @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      Action
 * @see      XMLOutputter
 */

class HTMLOutputter extends XMLOutputter
{
    /**
     * Constructor
     *
     * Just wraps the XMLOutputter constructor.
     *
     * @param string  $output URI to output to, default = stdout
     * @param boolean $indent Whether to indent output, default true
     */

    function __construct($output='php://output', $indent=null)
    {
        parent::__construct($output, $indent);
    }

    /**
     * Start an HTML document
     *
     * If $type isn't specified, will attempt to do content negotiation.
     *
     * Attempts to do content negotiation for language, also.
     *
     * @param string $type MIME type to use; default is to do negotation.
     *
     * @todo extract content negotiation code to an HTTP module or class.
     *
     * @return void
     */

    function startHTML($type=null)
    {
        if (!$type) {
            $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ?
              $_SERVER['HTTP_ACCEPT'] : null;

            // XXX: allow content negotiation for RDF, RSS, or XRDS

            $cp = common_accept_to_prefs($httpaccept);
            $sp = common_accept_to_prefs(PAGE_TYPE_PREFS);

            $type = common_negotiate_type($cp, $sp);

            if (!$type) {
                // TRANS: Client exception 406
                throw new ClientException(_('This page is not available in a '.
                                            'media type you accept'), 406);
            }
        }

        header('Content-Type: '.$type);

	// Output anti-framing headers to prevent clickjacking (respected by newer
        // browsers).
	if (common_config('javascript', 'bustframes')) {
            header('X-XSS-Protection: 1; mode=block'); // detect XSS Reflection attacks
            header('X-Frame-Options: SAMEORIGIN'); // no rendering if origin mismatch
        }

        $this->extraHeaders();
        if (preg_match("/.*\/.*xml/", $type)) {
            // Required for XML documents
            $this->startXML();
        }
        $this->xw->writeDTD('html',
                            '-//W3C//DTD XHTML 1.0 Strict//EN',
                            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd');

        $language = $this->getLanguage();

        $attrs = array(
            'xmlns' => 'http://www.w3.org/1999/xhtml',
            'xml:lang' => $language,
            'lang' => $language
        );

        if (Event::handle('StartHtmlElement', array($this, &$attrs))) {
            $this->elementStart('html', $attrs);
            Event::handle('EndHtmlElement', array($this, &$attrs));
        }
    }

    function getLanguage()
    {
        // FIXME: correct language for interface
        return common_language();
    }

    /**
    *  Ends an HTML document
    *
    *  @return void
    */
    function endHTML()
    {
        $this->elementEnd('html');
        $this->endXML();
    }

    /**
    *  To specify additional HTTP headers for the action
    *
    *  @return void
    */
    function extraHeaders()
    {
        // Needs to be overloaded
    }

    /**
     * Output an HTML text input element
     *
     * Despite the name, it is specifically for outputting a
     * text input element, not other <input> elements. It outputs
     * a cluster of elements, including a <label> and an associated
     * instructions span.
     *
     * If $attrs['type'] does not exist it will be set to 'text'.
     *
     * @param string $id           element ID, must be unique on page
     * @param string $label        text of label for the element
     * @param string $value        value of the element, default null
     * @param string $instructions instructions for valid input
     * @param string $name         name of the element; if null, the id will
     *                             be used
     * @param bool   $required     HTML5 required attribute (exclude when false)
     * @param array  $attrs        Initial attributes manually set in an array (overwritten by previous options)
     *
     * @todo add a $maxLength parameter
     * @todo add a $size parameter
     *
     * @return void
     */

    function input($id, $label, $value=null, $instructions=null, $name=null, $required=false, array $attrs=array())
    {
        $this->element('label', array('for' => $id), $label);
        if (!array_key_exists('type', $attrs)) {
            $attrs['type'] = 'text';
        }
        $attrs['id'] = $id;
        $attrs['name'] = is_null($name) ? $id : $name;
        if (!is_null($value)) { // value can be 0 or ''
            $attrs['value'] = $value;
        }
        if (!empty($required)) {
            $attrs['required'] = 'required';
        }
        $this->element('input', $attrs);
        if ($instructions) {
            $this->element('p', 'form_guide', $instructions);
        }
    }

    /**
     * output an HTML checkbox and associated elements
     *
     * Note that the value is default 'true' (the string), which can
     * be used by Action::boolean()
     *
     * @param string $id           element ID, must be unique on page
     * @param string $label        text of label for the element
     * @param string $checked      if the box is checked, default false
     * @param string $instructions instructions for valid input
     * @param string $value        value of the checkbox, default 'true'
     * @param string $disabled     show the checkbox disabled, default false
     *
     * @return void
     *
     * @todo add a $name parameter
     */

    function checkbox($id, $label, $checked=false, $instructions=null,
                      $value='true', $disabled=false)
    {
        $attrs = array('name' => $id,
                       'type' => 'checkbox',
                       'class' => 'checkbox',
                       'id' => $id);
        if ($value) {
            $attrs['value'] = $value;
        }
        if ($checked) {
            $attrs['checked'] = 'checked';
        }
        if ($disabled) {
            $attrs['disabled'] = 'true';
        }
        $this->element('input', $attrs);
        $this->text(' ');
        $this->element('label', array('class' => 'checkbox',
                                      'for' => $id),
                       $label);
        $this->text(' ');
        if ($instructions) {
            $this->element('p', 'form_guide', $instructions);
        }
    }

    /**
     * output an HTML combobox/select and associated elements
     *
     * $content is an array of key-value pairs for the dropdown, where
     * the key is the option value attribute and the value is the option
     * text. (Careful on the overuse of 'value' here.)
     *
     * @param string $id           element ID, must be unique on page
     * @param string $label        text of label for the element
     * @param array  $content      options array, value => text
     * @param string $instructions instructions for valid input
     * @param string $blank_select whether to have a blank entry, default false
     * @param string $selected     selected value, default null
     *
     * @return void
     *
     * @todo add a $name parameter
     */

    function dropdown($id, $label, $content, $instructions=null,
                      $blank_select=false, $selected=null)
    {
        $this->element('label', array('for' => $id), $label);
        $this->elementStart('select', array('id' => $id, 'name' => $id));
        if ($blank_select) {
            $this->element('option', array('value' => ''));
        }
        foreach ($content as $value => $option) {
            if ($value == $selected) {
                $this->element('option', array('value' => $value,
                                               'selected' => 'selected'),
                               $option);
            } else {
                $this->element('option', array('value' => $value), $option);
            }
        }
        $this->elementEnd('select');
        if ($instructions) {
            $this->element('p', 'form_guide', $instructions);
        }
    }

    /**
     * output an HTML hidden element
     *
     * $id is re-used as name
     *
     * @param string $id    element ID, must be unique on page
     * @param string $value hidden element value, default null
     * @param string $name  name, if different than ID
     *
     * @return void
     */

    function hidden($id, $value, $name=null)
    {
        $this->element('input', array('name' => $name ?: $id,
                                      'type' => 'hidden',
                                      'id' => $id,
                                      'value' => $value));
    }

    /**
     * output an HTML password input and associated elements
     *
     * @param string $id           element ID, must be unique on page
     * @param string $label        text of label for the element
     * @param string $instructions instructions for valid input
     *
     * @return void
     *
     * @todo add a $name parameter
     */

    function password($id, $label, $instructions=null)
    {
        $this->element('label', array('for' => $id), $label);
        $attrs = array('name' => $id,
                       'type' => 'password',
                       'class' => 'password',
                       'id' => $id);
        $this->element('input', $attrs);
        if ($instructions) {
            $this->element('p', 'form_guide', $instructions);
        }
    }

    /**
     * output an HTML submit input and associated elements
     *
     * @param string $id    element ID, must be unique on page
     * @param string $label text of the button
     * @param string $cls   class of the button, default 'submit'
     * @param string $name  name, if different than ID
     * @param string $title  title text for the submit button
     *
     * @return void
     *
     * @todo add a $name parameter
     */

    function submit($id, $label, $cls='submit', $name=null, $title=null)
    {
        $this->element('input', array('type' => 'submit',
                                      'id' => $id,
                                      'name'  => $name ?: $id,
                                      'class' => $cls,
                                      'value' => $label,
                                      'title' => $title));
    }

    /**
     * output a script (almost always javascript) tag
     *
     * @param string $src          relative or absolute script path
     * @param string $type         'type' attribute value of the tag
     *
     * @return void
     */
    function script($src, $type='text/javascript')
    {
        if (Event::handle('StartScriptElement', array($this,&$src,&$type))) {

            $url = parse_url($src);

            if (empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) {

                // XXX: this seems like a big assumption

                if (strpos($src, 'plugins/') === 0 || strpos($src, 'local/') === 0) {

                    $src = common_path($src, StatusNet::isHTTPS()) . '?version=' . GNUSOCIAL_VERSION;

                } else {

                    if (StatusNet::isHTTPS()) {

                        $sslserver = common_config('javascript', 'sslserver');

                        if (empty($sslserver)) {
                            if (is_string(common_config('site', 'sslserver')) &&
                                mb_strlen(common_config('site', 'sslserver')) > 0) {
                                $server = common_config('site', 'sslserver');
                            } else if (common_config('site', 'server')) {
                                $server = common_config('site', 'server');
                            }
                            $path   = common_config('site', 'path') . '/js/';
                        } else {
                            $server = $sslserver;
                            $path   = common_config('javascript', 'sslpath');
                            if (empty($path)) {
                                $path = common_config('javascript', 'path');
                            }
                        }

                        $protocol = 'https';

                    } else {

                        $path = common_config('javascript', 'path');

                        if (empty($path)) {
                            $path = common_config('site', 'path') . '/js/';
                        }

                        $server = common_config('javascript', 'server');

                        if (empty($server)) {
                            $server = common_config('site', 'server');
                        }

                        $protocol = 'http';
                    }

                    if ($path[strlen($path)-1] != '/') {
                        $path .= '/';
                    }

                    if ($path[0] != '/') {
                        $path = '/'.$path;
                    }

                    $src = $protocol.'://'.$server.$path.$src . '?version=' . GNUSOCIAL_VERSION;
                }
            }

            $this->element('script', array('type' => $type,
                                           'src' => $src),
                           ' ');

            Event::handle('EndScriptElement', array($this,$src,$type));
        }
    }

    /**
     * output a script (almost always javascript) tag with inline
     * code.
     *
     * @param string $code         code to put in the script tag
     * @param string $type         'type' attribute value of the tag
     *
     * @return void
     */

    function inlineScript($code, $type='text/javascript')
    {
        if(Event::handle('StartInlineScriptElement', array($this,&$code,&$type))) {
            $this->elementStart('script', array('type' => $type));
            if($type == 'text/javascript') {
                $this->raw('/*<![CDATA[*/ '); // XHTML compat
            }
            $this->raw($code);
            if($type == 'text/javascript') {
                $this->raw(' /*]]>*/'); // XHTML compat
            }
            $this->elementEnd('script');
            Event::handle('EndInlineScriptElement', array($this,$code,$type));
        }
    }

    /**
     * output a css link
     *
     * @param string $src     relative path within the theme directory, or an absolute path
     * @param string $theme        'theme' that contains the stylesheet
     * @param string media         'media' attribute of the tag
     *
     * @return void
     */
    function cssLink($src,$theme=null,$media=null)
    {
        if(Event::handle('StartCssLinkElement', array($this,&$src,&$theme,&$media))) {
            $url = parse_url($src);
            if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment']))
            {
                if(file_exists(Theme::file($src,$theme))){
                   $src = Theme::path($src, $theme);
                }else{
                    $src = common_path($src, StatusNet::isHTTPS());
                }
                $src.= '?version=' . GNUSOCIAL_VERSION;
            }
            $this->element('link', array('rel' => 'stylesheet',
                                    'type' => 'text/css',
                                    'href' => $src,
                                    'media' => $media));
            Event::handle('EndCssLinkElement', array($this,$src,$theme,$media));
        }
    }

    /**
     * output a style (almost always css) tag with inline
     * code.
     *
     * @param string $code         code to put in the style tag
     * @param string $type         'type' attribute value of the tag
     * @param string $media        'media' attribute value of the tag
     *
     * @return void
     */

    function style($code, $type = 'text/css', $media = null)
    {
        if(Event::handle('StartStyleElement', array($this,&$code,&$type,&$media))) {
            $this->elementStart('style', array('type' => $type, 'media' => $media));
            $this->raw($code);
            $this->elementEnd('style');
            Event::handle('EndStyleElement', array($this,$code,$type,$media));
        }
    }

    /**
     * output an HTML textarea and associated elements
     *
     * @param string $id           element ID, must be unique on page
     * @param string $label        text of label for the element
     * @param string $content      content of the textarea, default none
     * @param string $instructions instructions for valid input
     * @param string $name         name of textarea; if null, $id will be used
     * @param int    $cols         number of columns
     * @param int    $rows         number of rows
     * @param bool   $required     HTML5 required attribute (exclude when false)
     *
     * @return void
     */

    function textarea(
        $id,
        $label,
        $content      = null,
        $instructions = null,
        $name         = null,
        $cols         = null,
        $rows         = null,
        $required     = false
    ) {
        $this->element('label', array('for' => $id), $label);
        $attrs = array(
            'rows' => 3,
            'cols' => 40,
            'id' => $id
        );
        $attrs['name'] = is_null($name) ? $id : $name;

        if ($cols != null) {
            $attrs['cols'] = $cols;

        }
        if ($rows != null) {
            $attrs['rows'] = $rows;
        }
        $this->element(
            'textarea',
            $attrs,
            is_null($content) ? '' : $content
        );
        if ($instructions) {
            $this->element('p', 'form_guide', $instructions);
        }
    }

   /**
    * Internal script to autofocus the given element on page onload.
    *
    * @param string $id element ID, must refer to an existing element
    *
    * @return void
    *
    */
    function autofocus($id)
    {
        $this->inlineScript(
                   ' $(document).ready(function() {'.
                   ' var el = $("#' . $id . '");'.
                   ' if (el.length) { el.focus(); }'.
                   ' });');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Utility for doing HTTP-related things
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Action
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

require_once 'HTTP/Request2.php';
require_once 'HTTP/Request2/Response.php';

/**
 * Useful structure for HTTP responses
 *
 * We make HTTP calls in several places, and we have several different
 * ways of doing them. This class hides the specifics of what underlying
 * library (curl or PHP-HTTP or whatever) that's used.
 *
 * This extends the HTTP_Request2_Response class with methods to get info
 * about any followed redirects.
 *
 * Originally used the name 'HTTPResponse' to match earlier code, but
 * this conflicts with a class in in the PECL HTTP extension.
 *
 * @category HTTP
 * @package StatusNet
 * @author Evan Prodromou <evan@status.net>
 * @author Brion Vibber <brion@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 GNUsocial_HTTPResponse extends HTTP_Request2_Response
{
    function __construct(HTTP_Request2_Response $response, $url, $redirects=0)
    {
        foreach (get_object_vars($response) as $key => $val) {
            $this->$key = $val;
        }
        $this->url = strval($url);
        $this->redirectCount = intval($redirects);
    }

    /**
     * Get the count of redirects that have been followed, if any.
     * @return int
     */
    function getRedirectCount()
    {
        return $this->redirectCount;
    }

    /**
     * Gets the final target URL, after any redirects have been followed.
     * @return string URL
     */
    function getUrl()
    {
        return $this->url;
    }

    /**
     * Check if the response is OK, generally a 200 or other 2xx status code.
     * @return bool
     */
    function isOk()
    {
        $status = $this->getStatus();
        return ($status >= 200 && $status < 300);
    }
}

/**
 * Utility class for doing HTTP client stuff
 *
 * We make HTTP calls in several places, and we have several different
 * ways of doing them. This class hides the specifics of what underlying
 * library (curl or PHP-HTTP or whatever) that's used.
 *
 * This extends the PEAR HTTP_Request2 package:
 * - sends StatusNet-specific User-Agent header
 * - 'follow_redirects' config option, defaulting off
 * - 'max_redirs' config option, defaulting to 10
 * - extended response class adds getRedirectCount() and getUrl() methods
 * - get() and post() convenience functions return body content directly
 *
 * @category HTTP
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Brion Vibber <brion@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 HTTPClient extends HTTP_Request2
{

    function __construct($url=null, $method=self::METHOD_GET, $config=array())
    {
        $this->config['max_redirs'] = 10;
        $this->config['follow_redirects'] = true;

        // We've had some issues with keepalive breaking with
        // HEAD requests, such as to youtube which seems to be
        // emitting chunked encoding info for an empty body
        // instead of not emitting anything. This may be a
        // bug on YouTube's end, but the upstream libray
        // ought to be investigated to see if we can handle
        // it gracefully in that case as well.
        $this->config['protocol_version'] = '1.0';

        // Default state of OpenSSL seems to have no trusted
        // SSL certificate authorities, which breaks hostname
        // verification and means we have a hard time communicating
        // with other sites' HTTPS interfaces.
        //
        // Turn off verification unless we've configured a CA bundle.
        if (common_config('http', 'ssl_cafile')) {
            $this->config['ssl_cafile'] = common_config('http', 'ssl_cafile');
        } else {
            $this->config['ssl_verify_peer'] = false;
        }

        // This means "verify the cert hostname against what we connect to", it does not
        // imply CA trust or anything like that. Just the hostname.
        $this->config['ssl_verify_host'] = common_config('http', 'ssl_verify_host');

        if (common_config('http', 'curl') && extension_loaded('curl')) {
            $this->config['adapter'] = 'HTTP_Request2_Adapter_Curl';
        }

        foreach (array('host', 'port', 'user', 'password', 'auth_scheme') as $cf) {
            $k = 'proxy_'.$cf;
            $v = common_config('http', $k);
            if (!empty($v)) {
                $this->config[$k] = $v;
            }
        }

        parent::__construct($url, $method, $config);
        $this->setHeader('User-Agent', self::userAgent());
    }

    /**
     * Convenience/back-compat instantiator
     * @return HTTPClient
     */
    public static function start()
    {
        return new HTTPClient();
    }

    /**
     * Quick static function to GET a URL
     */
    public static function quickGet($url, $accept=null)
    {
        $client = new HTTPClient();
        if (!is_null($accept)) {
            $client->setHeader('Accept', $accept);
        }
        $response = $client->get($url);
        if (!$response->isOk()) {
            // TRANS: Exception. %s is a profile URL.
            throw new Exception(sprintf(_m('Could not GET URL %s.'), $url), $response->getStatus());
        }
        return $response->getBody();
    }

    /**
     * Convenience function to run a GET request.
     *
     * @return GNUsocial_HTTPResponse
     * @throws HTTP_Request2_Exception
     */
    public function get($url, $headers=array())
    {
        return $this->doRequest($url, self::METHOD_GET, $headers);
    }

    /**
     * Convenience function to run a HEAD request.
     *
     * @return GNUsocial_HTTPResponse
     * @throws HTTP_Request2_Exception
     */
    public function head($url, $headers=array())
    {
        return $this->doRequest($url, self::METHOD_HEAD, $headers);
    }

    /**
     * Convenience function to POST form data.
     *
     * @param string $url
     * @param array $headers optional associative array of HTTP headers
     * @param array $data optional associative array or blob of form data to submit
     * @return GNUsocial_HTTPResponse
     * @throws HTTP_Request2_Exception
     */
    public function post($url, $headers=array(), $data=array())
    {
        if ($data) {
            $this->addPostParameter($data);
        }
        return $this->doRequest($url, self::METHOD_POST, $headers);
    }

    /**
     * @return GNUsocial_HTTPResponse
     * @throws HTTP_Request2_Exception
     */
    protected function doRequest($url, $method, $headers)
    {
        $this->setUrl($url);

        // Workaround for HTTP_Request2 not setting up SNI in socket contexts;
        // This fixes cert validation for SSL virtual hosts using SNI.
        // Requires PHP 5.3.2 or later and OpenSSL with SNI support.
        if ($this->url->getScheme() == 'https' && defined('OPENSSL_TLSEXT_SERVER_NAME')) {
            $this->config['ssl_SNI_enabled'] = true;
            $this->config['ssl_SNI_server_name'] = $this->url->getHost();
        }

        $this->setMethod($method);
        if ($headers) {
            foreach ($headers as $header) {
                $this->setHeader($header);
            }
        }
        $response = $this->send();
        return $response;
    }

    protected function log($level, $detail) {
        $method = $this->getMethod();
        $url = $this->getUrl();
        common_log($level, __CLASS__ . ": HTTP $method $url - $detail");
    }

    /**
     * Pulls up GNU Social's customized user-agent string, so services
     * we hit can track down the responsible software.
     *
     * @return string
     */
    static public function userAgent()
    {
        return GNUSOCIAL_ENGINE . '/' . GNUSOCIAL_VERSION
                . ' (' . GNUSOCIAL_CODENAME . ')';
    }

    /**
     * Actually performs the HTTP request and returns a
     * GNUsocial_HTTPResponse object with response body and header info.
     *
     * Wraps around parent send() to add logging and redirection processing.
     *
     * @return GNUsocial_HTTPResponse
     * @throw HTTP_Request2_Exception
     */
    public function send()
    {
        $maxRedirs = intval($this->config['max_redirs']);
        if (empty($this->config['follow_redirects'])) {
            $maxRedirs = 0;
        }
        $redirs = 0;
        do {
            try {
                $response = parent::send();
            } catch (HTTP_Request2_Exception $e) {
                $this->log(LOG_ERR, $e->getMessage());
                throw $e;
            }
            $code = $response->getStatus();
            if ($code >= 200 && $code < 300) {
                $reason = $response->getReasonPhrase();
                $this->log(LOG_INFO, "$code $reason");
            } elseif ($code >= 300 && $code < 400) {
                $url = $this->getUrl();
                $target = $response->getHeader('Location');

                if (++$redirs >= $maxRedirs) {
                    common_log(LOG_ERR, __CLASS__ . ": Too many redirects: skipping $code redirect from $url to $target");
                    break;
                }
                try {
                    $this->setUrl($target);
                    $this->setHeader('Referer', $url);
                    common_log(LOG_INFO, __CLASS__ . ": Following $code redirect from $url to $target");
                    continue;
                } catch (HTTP_Request2_Exception $e) {
                    common_log(LOG_ERR, __CLASS__ . ": Invalid $code redirect from $url to $target");
                }
            } else {
                $reason = $response->getReasonPhrase();
                $this->log(LOG_ERR, "$code $reason");
            }
            break;
        } while ($maxRedirs);
        return new GNUsocial_HTTPResponse($response, $this->getUrl(), $redirs);
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Abstraction for an image file
 *
 * 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  Image
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Zach Copley <zach@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * A wrapper on uploaded files
 *
 * Makes it slightly easier to accept an image file from upload.
 *
 * @category Image
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @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 ImageFile
{
    var $id;
    var $filepath;
    var $filename;
    var $type;
    var $height;
    var $width;
    var $rotate=0;  // degrees to rotate for properly oriented image (extrapolated from EXIF etc.)
    var $animated = null;  // Animated image? (has more than 1 frame). null means untested

    function __construct($id=null, $filepath=null, $type=null, $width=null, $height=null)
    {
        $this->id = $id;
        $this->filepath = $filepath;
        $this->filename = basename($filepath);

        $info = @getimagesize($this->filepath);

        if (!(
            ($info[2] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) ||
            ($info[2] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) ||
            $info[2] == IMAGETYPE_BMP ||
            ($info[2] == IMAGETYPE_WBMP && function_exists('imagecreatefromwbmp')) ||
            ($info[2] == IMAGETYPE_XBM && function_exists('imagecreatefromxbm')) ||
            ($info[2] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')))) {

            // TRANS: Exception thrown when trying to upload an unsupported image file format.
            throw new UnsupportedMediaException(_('Unsupported image format.'), $this->filepath);
        }

        $this->type = ($info) ? $info[2]:$type;
        $this->width = ($info) ? $info[0]:$width;
        $this->height = ($info) ? $info[1]:$height;

        if ($this->type == IMAGETYPE_JPEG && function_exists('exif_read_data')) {
            // Orientation value to rotate thumbnails properly
            $exif = exif_read_data($this->filepath);
            if (is_array($exif) && isset($exif['Orientation'])) {
                switch ((int)$exif['Orientation']) {
                case 1: // top is top
                    $this->rotate = 0;
                    break;
                case 3: // top is bottom
                    $this->rotate = 180;
                    break;
                case 6: // top is right
                    $this->rotate = -90;
                    break;
                case 8: // top is left
                    $this->rotate = 90;
                    break;
                }
                // If we ever write this back, Orientation should be set to '1'
            }
        } elseif ($this->type === IMAGETYPE_GIF) {
            $this->animated = $this->isAnimatedGif();
        }

        Event::handle('FillImageFileMetadata', array($this));
    }

    public static function fromFileObject(File $file)
    {
        $imgPath = null;
        $media = common_get_mime_media($file->mimetype);
        if (Event::handle('CreateFileImageThumbnailSource', array($file, &$imgPath, $media))) {
            if (empty($file->filename)) {
                throw new UnsupportedMediaException(_('File without filename could not get a thumbnail source.'));
            }
            switch ($media) {
            case 'image':
                $imgPath = $file->getPath();
                break;
            default:
                throw new UnsupportedMediaException(_('Unsupported media format.'), $file->getPath());
            }
        }

        if (!file_exists($imgPath)) {
            throw new ServerException(sprintf('Image not available locally: %s', $imgPath));
        }

        try {
            $image = new ImageFile($file->id, $imgPath);
        } catch (UnsupportedMediaException $e) {
            // Avoid deleting the original
            if ($imgPath != $file->getPath()) {
                unlink($imgPath);
            }
            throw $e;
        }
        return $image;
    }

    public function getPath()
    {
        if (!file_exists($this->filepath)) {
            throw new ServerException('No file in ImageFile filepath');
        }

        return $this->filepath;
    }

    static function fromUpload($param='upload')
    {
        switch ($_FILES[$param]['error']) {
         case UPLOAD_ERR_OK: // success, jump out
            break;

         case UPLOAD_ERR_INI_SIZE:
         case UPLOAD_ERR_FORM_SIZE:
            // TRANS: Exception thrown when too large a file is uploaded.
            // TRANS: %s is the maximum file size, for example "500b", "10kB" or "2MB".
            throw new Exception(sprintf(_('That file is too big. The maximum file size is %s.'), ImageFile::maxFileSize()));

         case UPLOAD_ERR_PARTIAL:
            @unlink($_FILES[$param]['tmp_name']);
            // TRANS: Exception thrown when uploading an image and that action could not be completed.
            throw new Exception(_('Partial upload.'));

         case UPLOAD_ERR_NO_FILE:
            // No file; probably just a non-AJAX submission.
         default:
            common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " . $_FILES[$param]['error']);
            // TRANS: Exception thrown when uploading an image fails for an unknown reason.
            throw new Exception(_('System error uploading file.'));
        }

        $info = @getimagesize($_FILES[$param]['tmp_name']);

        if (!$info) {
            @unlink($_FILES[$param]['tmp_name']);
            // TRANS: Exception thrown when uploading a file as image that is not an image or is a corrupt file.
            throw new UnsupportedMediaException(_('Not an image or corrupt file.'), '[deleted]');
        }

        return new ImageFile(null, $_FILES[$param]['tmp_name']);
    }

    /**
     * Compat interface for old code generating avatar thumbnails...
     * Saves the scaled file directly into the avatar area.
     *
     * @param int $size target width & height -- must be square
     * @param int $x (default 0) upper-left corner to crop from
     * @param int $y (default 0) upper-left corner to crop from
     * @param int $w (default full) width of image area to crop
     * @param int $h (default full) height of image area to crop
     * @return string filename
     */
    function resize($size, $x = 0, $y = 0, $w = null, $h = null)
    {
        $targetType = $this->preferredType();
        $outname = Avatar::filename($this->id,
                                    image_type_to_extension($targetType),
                                    $size,
                                    common_timestamp());
        $outpath = Avatar::path($outname);
        $this->resizeTo($outpath, array('width'=>$size, 'height'=>$size,
                                        'x'=>$x,        'y'=>$y,
                                        'w'=>$w,        'h'=>$h));
        return $outname;
    }

    /**
     * Copy the image file to the given destination.
     *
     * This function may modify the resulting file. Please use the
     * returned ImageFile object to read metadata (width, height etc.)
     *
     * @param string $outpath
     * @return ImageFile the image stored at target path
     */
    function copyTo($outpath)
    {
        return new ImageFile(null, $this->resizeTo($outpath));
    }

    /**
     * Create and save a thumbnail image.
     *
     * @param string $outpath
     * @param array $box    width, height, boundary box (x,y,w,h) defaults to full image
     * @return string full local filesystem filename
     */
    function resizeTo($outpath, array $box=array())
    {
        $box['width'] = isset($box['width']) ? intval($box['width']) : $this->width;
        $box['height'] = isset($box['height']) ? intval($box['height']) : $this->height;
        $box['x'] = isset($box['x']) ? intval($box['x']) : 0;
        $box['y'] = isset($box['y']) ? intval($box['y']) : 0;
        $box['w'] = isset($box['w']) ? intval($box['w']) : $this->width;
        $box['h'] = isset($box['h']) ? intval($box['h']) : $this->height;

        if (!file_exists($this->filepath)) {
            // TRANS: Exception thrown during resize when image has been registered as present, but is no longer there.
            throw new Exception(_('Lost our file.'));
        }

        // Don't rotate/crop/scale if it isn't necessary
        if ($box['width'] === $this->width
                && $box['height'] === $this->height
                && $box['x'] === 0
                && $box['y'] === 0
                && $box['w'] === $this->width
                && $box['h'] === $this->height
                && $this->type == $this->preferredType()) {
            if ($this->rotate == 0) {
                // No rotational difference, just copy it as-is
                @copy($this->filepath, $outpath);
                return $outpath;
            } elseif (abs($this->rotate) == 90) {
                // Box is rotated 90 degrees in either direction,
                // so we have to redefine x to y and vice versa.
                $tmp = $box['width'];
                $box['width'] = $box['height'];
                $box['height'] = $tmp;
                $tmp = $box['x'];
                $box['x'] = $box['y'];
                $box['y'] = $tmp;
                $tmp = $box['w'];
                $box['w'] = $box['h'];
                $box['h'] = $tmp;
            }
        }

        if (Event::handle('StartResizeImageFile', array($this, $outpath, $box))) {
            $this->resizeToFile($outpath, $box);
        }

        if (!file_exists($outpath)) {
            throw new UseFileAsThumbnailException($this->id);
        }

        return $outpath;
    }

    protected function resizeToFile($outpath, array $box)
    {
        switch ($this->type) {
        case IMAGETYPE_GIF:
            $image_src = imagecreatefromgif($this->filepath);
            break;
        case IMAGETYPE_JPEG:
            $image_src = imagecreatefromjpeg($this->filepath);
            break;
        case IMAGETYPE_PNG:
            $image_src = imagecreatefrompng($this->filepath);
            break;
        case IMAGETYPE_BMP:
            $image_src = imagecreatefrombmp($this->filepath);
            break;
        case IMAGETYPE_WBMP:
            $image_src = imagecreatefromwbmp($this->filepath);
            break;
        case IMAGETYPE_XBM:
            $image_src = imagecreatefromxbm($this->filepath);
            break;
        default:
            // TRANS: Exception thrown when trying to resize an unknown file type.
            throw new Exception(_('Unknown file type'));
        }

        if ($this->rotate != 0) {
            $image_src = imagerotate($image_src, $this->rotate, 0);
        }

        $image_dest = imagecreatetruecolor($box['width'], $box['height']);

        if ($this->type == IMAGETYPE_GIF || $this->type == IMAGETYPE_PNG || $this->type == IMAGETYPE_BMP) {

            $transparent_idx = imagecolortransparent($image_src);

            if ($transparent_idx >= 0) {

                $transparent_color = imagecolorsforindex($image_src, $transparent_idx);
                $transparent_idx = imagecolorallocate($image_dest, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
                imagefill($image_dest, 0, 0, $transparent_idx);
                imagecolortransparent($image_dest, $transparent_idx);

            } elseif ($this->type == IMAGETYPE_PNG) {

                imagealphablending($image_dest, false);
                $transparent = imagecolorallocatealpha($image_dest, 0, 0, 0, 127);
                imagefill($image_dest, 0, 0, $transparent);
                imagesavealpha($image_dest, true);

            }
        }

        imagecopyresampled($image_dest, $image_src, 0, 0, $box['x'], $box['y'], $box['width'], $box['height'], $box['w'], $box['h']);

        switch ($this->preferredType()) {
         case IMAGETYPE_GIF:
            imagegif($image_dest, $outpath);
            break;
         case IMAGETYPE_JPEG:
            imagejpeg($image_dest, $outpath, common_config('image', 'jpegquality'));
            break;
         case IMAGETYPE_PNG:
            imagepng($image_dest, $outpath);
            break;
         default:
            // TRANS: Exception thrown when trying resize an unknown file type.
            throw new Exception(_('Unknown file type'));
        }

        imagedestroy($image_src);
        imagedestroy($image_dest);
    }

    /**
     * Several obscure file types should be normalized to PNG on resize.
     *
     * @fixme consider flattening anything not GIF or JPEG to PNG
     * @return int
     */
    function preferredType()
    {
        if($this->type == IMAGETYPE_BMP) {
            //we don't want to save BMP... it's an inefficient, rare, antiquated format
            //save png instead
            return IMAGETYPE_PNG;
        } else if($this->type == IMAGETYPE_WBMP) {
            //we don't want to save WBMP... it's a rare format that we can't guarantee clients will support
            //save png instead
            return IMAGETYPE_PNG;
        } else if($this->type == IMAGETYPE_XBM) {
            //we don't want to save XBM... it's a rare format that we can't guarantee clients will support
            //save png instead
            return IMAGETYPE_PNG;
        }
        return $this->type;
    }

    function unlink()
    {
        @unlink($this->filepath);
    }

    static function maxFileSize()
    {
        $value = ImageFile::maxFileSizeInt();

        if ($value > 1024 * 1024) {
            $value = $value/(1024*1024);
            // TRANS: Number of megabytes. %d is the number.
            return sprintf(_m('%dMB','%dMB',$value),$value);
        } else if ($value > 1024) {
            $value = $value/1024;
            // TRANS: Number of kilobytes. %d is the number.
            return sprintf(_m('%dkB','%dkB',$value),$value);
        } else {
            // TRANS: Number of bytes. %d is the number.
            return sprintf(_m('%dB','%dB',$value),$value);
        }
    }

    static function maxFileSizeInt()
    {
        return min(ImageFile::strToInt(ini_get('post_max_size')),
                   ImageFile::strToInt(ini_get('upload_max_filesize')),
                   ImageFile::strToInt(ini_get('memory_limit')));
    }

    static function strToInt($str)
    {
        $unit = substr($str, -1);
        $num = substr($str, 0, -1);

        switch(strtoupper($unit)){
         case 'G':
            $num *= 1024;
         case 'M':
            $num *= 1024;
         case 'K':
            $num *= 1024;
        }

        return $num;
    }

    public function scaleToFit($maxWidth=null, $maxHeight=null, $crop=null)
    {
        return self::getScalingValues($this->width, $this->height,
                                        $maxWidth, $maxHeight, $crop, $this->rotate);
    }

    /*
     * Gets scaling values for images of various types. Cropping can be enabled.
     *
     * Values will scale _up_ to fit max values if cropping is enabled!
     * With cropping disabled, the max value of each axis will be respected.
     *
     * @param $width    int Original width
     * @param $height   int Original height
     * @param $maxW     int Resulting max width
     * @param $maxH     int Resulting max height
     * @param $crop     int Crop to the size (not preserving aspect ratio)
     */
    public static function getScalingValues($width, $height,
                                        $maxW=null, $maxH=null,
                                        $crop=null, $rotate=0)
    {
        $maxW = $maxW ?: common_config('thumbnail', 'width');
        $maxH = $maxH ?: common_config('thumbnail', 'height');

        if ($maxW < 1 || ($maxH !== null && $maxH < 1)) {
            throw new ServerException('Bad parameters for ImageFile::getScalingValues');
        } elseif ($maxH === null) {
            // if maxH is null, we set maxH to equal maxW and enable crop
            $maxH = $maxW;
            $crop = true;
        }

        // Because GD doesn't understand EXIF orientation etc.
        if (abs($rotate) == 90) {
            $tmp = $width;
            $width = $height;
            $height = $tmp;
        }

        // Cropping data (for original image size). Default values, 0 and null,
        // imply no cropping and with preserved aspect ratio (per axis).
        $cx = 0;    // crop x
        $cy = 0;    // crop y
        $cw = null; // crop area width
        $ch = null; // crop area height

        if ($crop) {
            $s_ar = $width / $height;
            $t_ar = $maxW / $maxH;

            $rw = $maxW;
            $rh = $maxH;

            // Source aspect ratio differs from target, recalculate crop points!
            if ($s_ar > $t_ar) {
                $cx = floor($width / 2 - $height * $t_ar / 2);
                $cw = ceil($height * $t_ar);
            } elseif ($s_ar < $t_ar) {
                $cy = floor($height / 2 - $width / $t_ar / 2);
                $ch = ceil($width / $t_ar);
            }
        } else {
            $rw = $maxW;
            $rh = ceil($height * $rw / $width);

            // Scaling caused too large height, decrease to max accepted value
            if ($rh > $maxH) {
                $rh = $maxH;
                $rw = ceil($width * $rh / $height);
            }
        }
        return array(intval($rw), intval($rh),
                    intval($cx), intval($cy),
                    is_null($cw) ? $width : intval($cw),
                    is_null($ch) ? $height : intval($ch));
    }

    /**
     * Animated GIF test, courtesy of frank at huddler dot com et al:
     * http://php.net/manual/en/function.imagecreatefromgif.php#104473
     * Modified so avoid landing inside of a header (and thus not matching our regexp).
     */
    protected function isAnimatedGif()
    {
        if (!($fh = @fopen($this->filepath, 'rb'))) {
            return false;
        }

        $count = 0;
        //an animated gif contains multiple "frames", with each frame having a
        //header made up of:
        // * a static 4-byte sequence (\x00\x21\xF9\x04)
        // * 4 variable bytes
        // * a static 2-byte sequence (\x00\x2C)
        // In total the header is maximum 10 bytes.

        // We read through the file til we reach the end of the file, or we've found
        // at least 2 frame headers
        while(!feof($fh) && $count < 2) {
            $chunk = fread($fh, 1024 * 100); //read 100kb at a time
            $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00\x2C#s', $chunk, $matches);
            // rewind in case we ended up in the middle of the header, but avoid
            // infinite loop (i.e. don't rewind if we're already in the end).
            if (!feof($fh) && ftell($fh) >= 9) {
                fseek($fh, -9, SEEK_CUR);
            }
        }

        fclose($fh);
        return $count > 1;
    }
}

//PHP doesn't (as of 2/24/2010) have an imagecreatefrombmp so conditionally define one
if(!function_exists('imagecreatefrombmp')){
    //taken shamelessly from http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
    function imagecreatefrombmp($p_sFile)
    {
        //    Load the image into a string
        $file    =    fopen($p_sFile,"rb");
        $read    =    fread($file,10);
        while(!feof($file)&&($read<>""))
            $read    .=    fread($file,1024);

        $temp    =    unpack("H*",$read);
        $hex    =    $temp[1];
        $header    =    substr($hex,0,108);

        //    Process the header
        //    Structure: http://www.fastgraph.com/help/bmp_header_format.html
        if (substr($header,0,4)=="424d")
        {
            //    Cut it in parts of 2 bytes
            $header_parts    =    str_split($header,2);

            //    Get the width        4 bytes
            $width            =    hexdec($header_parts[19].$header_parts[18]);

            //    Get the height        4 bytes
            $height            =    hexdec($header_parts[23].$header_parts[22]);

            //    Unset the header params
            unset($header_parts);
        }

        //    Define starting X and Y
        $x                =    0;
        $y                =    1;

        //    Create newimage
        $image            =    imagecreatetruecolor($width,$height);

        //    Grab the body from the image
        $body            =    substr($hex,108);

        //    Calculate if padding at the end-line is needed
        //    Divided by two to keep overview.
        //    1 byte = 2 HEX-chars
        $body_size        =    (strlen($body)/2);
        $header_size    =    ($width*$height);

        //    Use end-line padding? Only when needed
        $usePadding        =    ($body_size>($header_size*3)+4);

        //    Using a for-loop with index-calculation instaid of str_split to avoid large memory consumption
        //    Calculate the next DWORD-position in the body
        for ($i=0;$i<$body_size;$i+=3)
        {
            //    Calculate line-ending and padding
            if ($x>=$width)
            {
                //    If padding needed, ignore image-padding
                //    Shift i to the ending of the current 32-bit-block
                if ($usePadding)
                    $i    +=    $width%4;

                //    Reset horizontal position
                $x    =    0;

                //    Raise the height-position (bottom-up)
                $y++;

                //    Reached the image-height? Break the for-loop
                if ($y>$height)
                    break;
            }

            //    Calculation of the RGB-pixel (defined as BGR in image-data)
            //    Define $i_pos as absolute position in the body
            $i_pos    =    $i*2;
            $r        =    hexdec($body[$i_pos+4].$body[$i_pos+5]);
            $g        =    hexdec($body[$i_pos+2].$body[$i_pos+3]);
            $b        =    hexdec($body[$i_pos].$body[$i_pos+1]);

            //    Calculate and draw the pixel
            $color    =    imagecolorallocate($image,$r,$g,$b);
            imagesetpixel($image,$x,$height-$y,$color);

            //    Raise the horizontal position
            $x++;
        }

        //    Unset the body / free the memory
        unset($body);

        //    Return image-object
        return $image;
    }
}   // if(!function_exists('imagecreatefrombmp'))


code:
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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

class IMChannel extends Channel
{

    var $imPlugin;

    function source()
    {
        return $imPlugin->transport;
    }

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

    function on($user)
    {
        return $this->setNotify($user, 1);
    }

    function off($user)
    {
        return $this->setNotify($user, 0);
    }

    function output($user, $text)
    {
        $text = '['.common_config('site', 'name') . '] ' . $text;
        $this->imPlugin->sendMessage($this->imPlugin->getScreenname($user), $text);
    }

    function error($user, $text)
    {
        $text = '['.common_config('site', 'name') . '] ' . $text;

        $screenname = $this->imPlugin->getScreenname($user);
        if($screenname){
            $this->imPlugin->sendMessage($screenname, $text);
            return true;
        }else{
            common_log(LOG_ERR,
                'Could not send error message to user ' . common_log_objstring($user) .
                ' on transport ' . $this->imPlugin->transport .' : user preference does not exist');
            return false;
        }
    }

    function setNotify($user, $notify)
    {
        global $_PEAR;

        $user_im_prefs = new User_im_prefs();
        $user_im_prefs->transport = $this->imPlugin->transport;
        $user_im_prefs->user_id = $user->id;
        if($user_im_prefs->find() && $user_im_prefs->fetch()){
            if($user_im_prefs->notify == $notify){
                //notify is already set the way they want
                return true;
            }else{
                $original = clone($user_im_prefs);
                $user_im_prefs->notify = $notify;
                $result = $user_im_prefs->update($original);

                if (!$result) {
                    $last_error = &$_PEAR->getStaticProperty('DB_DataObject','lastError');
                    common_log(LOG_ERR,
                               'Could not set notify flag to ' . $notify .
                               ' for user ' . common_log_objstring($user) .
                               ' on transport ' . $this->imPlugin->transport .' : ' . $last_error->message);
                    return false;
                } else {
                    common_log(LOG_INFO,
                               'User ' . $user->nickname . ' set notify flag to ' . $notify);
                    return true;
                }
            }
        }else{
                common_log(LOG_ERR,
                           'Could not set notify flag to ' . $notify .
                           ' for user ' . common_log_objstring($user) .
                           ' on transport ' . $this->imPlugin->transport .' : user preference does not exist');
                return false;
        }
    }
}


code:
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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

/**
 * IKM background connection manager for IM-using queue handlers,
 * allowing them to send outgoing messages on the right connection.
 *
 * In a multi-site queuedaemon.php run, one connection will be instantiated
 * for each site being handled by the current process that has IM enabled.
 *
 * Implementations that extend this class will likely want to:
 * 1) override start() with their connection process.
 * 2) override handleInput() with what to do when data is waiting on
 *    one of the sockets
 * 3) override idle($timeout) to do keepalives (if necessary)
 * 4) implement send_raw_message() to send raw data that ImPlugin::enqueueOutgoingRaw
 *      enqueued
 */

abstract class ImManager extends IoManager
{
    abstract function send_raw_message($data);

    function __construct($imPlugin)
    {
        $this->plugin = $imPlugin;
        $this->plugin->imManager = $this;
    }

    /**
     * Fetch the singleton manager for the current site.
     * @return mixed ImManager, or false if unneeded
     */
    public static function get()
    {
        throw new Exception('ImManager should be created using it\'s constructor, not the static get method');
    }
}