code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:19+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Tamil (http://www.transifex.com/gnu-social/gnu-social/language/ta/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ta\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr ""

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr ""

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr ""

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:19+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Telugu (http://www.transifex.com/gnu-social/gnu-social/language/te/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: te\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr ""

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr ""

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr ""

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""

repo_name: RMichael/gnu

license: mit


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:34+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Tagalog (http://www.transifex.com/gnu-social/gnu-social/language/tl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tl\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr "Dumarampot ng abatar at ibang kabatiran ng balangkas para sa mga tagagamit ng WikiHow kapag nagtatalaga ng isang akawnt sa pamamagitan ng OpenID."

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr "Nabigo ang pagsalok sa pahina ng balangkas ng WikiHow."

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr "Kabiguan ng parse ng HTML habang sinusuri para sa abatar ng WikiHow."

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr "Hindi tanggap na URL ng abatar ang %s."

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr "Hindi nagawang damputin ang huwaran mula sa %s."


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Uğur KUYU <ukuyu34@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-03-02 10:40+0000\n"
"Last-Translator: Uğur KUYU <ukuyu34@gmail.com>\n"
"Language-Team: Turkish (http://www.transifex.com/gnu-social/gnu-social/language/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr "OpenID aracılığıyla hesap oluşturan WikiHow kullanıcıları için kullanıcı resimlerini ve diğer profil bilgilerini alır."

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr "WikiHow profil sayfası alımı başarısız oldu"

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr "WikiHow avatarı onayı sırasında HTML ayrıştırma hatası"

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr "Geçersiz kullanıcı resmi bağlantısı %s."

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr "%s'ten kullanıcı resmi alınamıyor."


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Петро Романчук <ixer@ixer.org.ua>, 2015
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-03-31 11:12+0000\n"
"Last-Translator: Петро Романчук <ixer@ixer.org.ua>\n"
"Language-Team: Ukrainian (http://www.transifex.com/gnu-social/gnu-social/language/uk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: uk\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr "Запитує аватар та іншу супутню інформацію з WikiHow для користувачів, котрі створюють акаунти через OpenID."

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr "Не вдалось отримати інформацію зі сторінки профілю WikiHow."

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr "Помилка обробки HTML  під час перевірки аватару з WikiHow."

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr "Невірна URL-адреса аватари %s."

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr "Неможливо завантажити аватару з %s."


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:19+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Urdu (Pakistan) (http://www.transifex.com/gnu-social/gnu-social/language/ur_PK/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ur_PK\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr ""

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr ""

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr ""

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:19+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Vietnamese (http://www.transifex.com/gnu-social/gnu-social/language/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr ""

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr ""

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr ""

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:19+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (http://www.transifex.com/gnu-social/gnu-social/language/zh/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr ""

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr ""

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr ""

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:19+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/gnu-social/gnu-social/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr ""

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr ""

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr ""

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 14:19+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/gnu-social/gnu-social/language/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin description.
#: WikiHowProfilePlugin.php:60
msgid ""
"Fetches avatar and other profile information for WikiHow users when setting "
"up an account via OpenID."
msgstr ""

#. TRANS: Exception thrown when fetching a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:126
msgid "WikiHow profile page fetch failed."
msgstr ""

#. TRANS: Exception thrown when parsing a WikiHow profile page fails.
#: WikiHowProfilePlugin.php:143
msgid "HTML parse failure during check for WikiHow avatar."
msgstr ""

#. TRANS: Server exception thrown when an avatar URL is invalid.
#. TRANS: %s is the invalid avatar URL.
#: WikiHowProfilePlugin.php:176
#, php-format
msgid "Invalid avatar URL %s."
msgstr ""

#. TRANS: Exception thrown when fetching an avatar from a URL fails.
#. TRANS: %s is a URL.
#: WikiHowProfilePlugin.php:186
#, php-format
msgid "Unable to fetch avatar from %s."
msgstr ""


code:
XMPP (eXtended Message and Presence Protocol, <http://xmpp.org/>) is the
federating instant-messaging protocol of the future. It is wildly used
all over the world by organisations, private individuals and everyone.

GNU social allows you to receive and distribute messages via XMPP using
this plugin. To get it running, you must also use an active XMPP account.

Installation
============
Add an addPlugin call to your config.php with your settings. Please read
the "Pre-requisites" section of what is required for this to work.

Example
-------
The example account "update@site.example" is hosted on a machine which
can be reached at the hostname "xmpp.site.example".

addPlugin('Xmpp', array(
    'user'      => 'update',
    'server'    => 'site.example',
    'host'      => 'xmpp.site.example',
    'password'  => '...',
));

Pre-requisites
==============

0. You may want to strongly consider setting up your own XMPP server.
   We highly recommend the XMPP server "Prosody" <https://prosody.im/>
   because it is actively developed and highly secure and efficient. It
   is of course also free software under the MIT license. The following
   three pages will help you get it running, even self-hosted at home:

      0.1 https://prosody.im/doc/dns
      0.2 https://prosody.im/doc/install
      0.3 https://prosody.im/doc/configure

1. You must register an XMPP user ID (JID) which is used to send and
   receive messages. Call it something like "update@site.example" or
   similar to hint at what the account is made for. You may register
   the account on any public server (jabber.org, jit.si, etc...) if
   you cannot run one yourself.

   GNU social will not register anything for you, this must be done
   manually, preferrably using an XMPP client like Swift, Empathy,
   Jitsi or maybe even the commandline on your own server. With
   prosody, that'd be (perhaps prepended with 'sudo'):

      prosodyctl adduser update@site.example

2. Configure your site's XMPP variables, as described below in the
   Settings section below.

3. Learn to use the GNU social daemons for processing notice queues,
   background checks and other processes which would be too slow to
   perform on an active site. Using XMPP requires the "imdaemon" to
   run, since a long-running XMPP connection is somewhat necessary.

Settings
========

Required
--------
user        User part of the jid (like 'update')
server      Host part of the jid (like 'site.example')
password    The account's password. (your secret string)

Optional
--------
resource    JID resource. Default: 'gnusocial'
encryption  TLS server? Default: true
host        Hostname for XMPP server? Default: same as server
port        XMPP server port. Default: 5222
debug       Log extra debug info. Default: false
public      JIDs that should get the public feed (see "Public feed").

Since we do not currently support DNS SRV record lookup, please note
that you may have to enter an alternative 'host' parameter. This is
the case when update@site.example is not handled by the direct _address_
"site.example" but rather something like "xmpp.site.example".

Public feed
===========

You can send *all* messages from your social networking site to a
third-party service using XMPP. This can be useful for providing
search, indexing, bridging, or other cool services. Maybe a text
display next to your coffee machine at work.

To configure a downstream site to receive your public stream, add
their "JID" (Jabber ID) in the "public" array in your addPlugin call.
For example

addPlugin(array(
    [...]
    'public' => array('awesomebot@site.example'),

As the Pre-requisites section says, please only try to configure this
with daemons running properly in the background.

path: plugins/Xmpp/README


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2009, StatusNet, Inc.
 *
 * Send and receive notices using the XMPP network
 *
 * 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  IM
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 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);
}

/**
 * Plugin for XMPP
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class XmppPlugin extends ImPlugin
{
    public $server = null;
    public $port = 5222;
    public $user =  'update';
    public $resource = 'gnusocial';
    public $encryption = true;
    public $password = null;
    public $host = null;  // only set if != server
    public $debug = false; // print extra debug info

    public $transport = 'xmpp';

    function getDisplayName(){
        // TRANS: Plugin display name.
        return _m('XMPP/Jabber');
    }

    /**
     * Splits a Jabber ID (JID) into node, domain, and resource portions.
     *
     * Based on validation routine submitted by:
     * @copyright 2009 Patrick Georgi <patrick@georgi-clan.de>
     * @license Licensed under ISC-L, which is compatible with everything else that keeps the copyright notice intact.
     *
     * @param string $jid string to check
     *
     * @return array with "node", "domain", and "resource" indices
     * @throws Exception if input is not valid
     */
    protected function splitJid($jid)
    {
        $chars = '';
        /* the following definitions come from stringprep, Appendix C,
           which is used in its entirety by nodeprop, Chapter 5, "Prohibited Output" */
        /* C1.1 ASCII space characters */
        $chars .= "\x{20}";
        /* C1.2 Non-ASCII space characters */
        $chars .= "\x{a0}\x{1680}\x{2000}-\x{200b}\x{202f}\x{205f}\x{3000a}";
        /* C2.1 ASCII control characters */
        $chars .= "\x{00}-\x{1f}\x{7f}";
        /* C2.2 Non-ASCII control characters */
        $chars .= "\x{80}-\x{9f}\x{6dd}\x{70f}\x{180e}\x{200c}\x{200d}\x{2028}\x{2029}\x{2060}-\x{2063}\x{206a}-\x{206f}\x{feff}\x{fff9}-\x{fffc}\x{1d173}-\x{1d17a}";
        /* C3 - Private Use */
        $chars .= "\x{e000}-\x{f8ff}\x{f0000}-\x{ffffd}\x{100000}-\x{10fffd}";
        /* C4 - Non-character code points */
        $chars .= "\x{fdd0}-\x{fdef}\x{fffe}\x{ffff}\x{1fffe}\x{1ffff}\x{2fffe}\x{2ffff}\x{3fffe}\x{3ffff}\x{4fffe}\x{4ffff}\x{5fffe}\x{5ffff}\x{6fffe}\x{6ffff}\x{7fffe}\x{7ffff}\x{8fffe}\x{8ffff}\x{9fffe}\x{9ffff}\x{afffe}\x{affff}\x{bfffe}\x{bffff}\x{cfffe}\x{cffff}\x{dfffe}\x{dffff}\x{efffe}\x{effff}\x{ffffe}\x{fffff}\x{10fffe}\x{10ffff}";
        /* C5 - Surrogate codes */
        $chars .= "\x{d800}-\x{dfff}";
        /* C6 - Inappropriate for plain text */
        $chars .= "\x{fff9}-\x{fffd}";
        /* C7 - Inappropriate for canonical representation */
        $chars .= "\x{2ff0}-\x{2ffb}";
        /* C8 - Change display properties or are deprecated */
        $chars .= "\x{340}\x{341}\x{200e}\x{200f}\x{202a}-\x{202e}\x{206a}-\x{206f}";
        /* C9 - Tagging characters */
        $chars .= "\x{e0001}\x{e0020}-\x{e007f}";

        /* Nodeprep forbids some more characters */
        $nodeprepchars = $chars;
        $nodeprepchars .= "\x{22}\x{26}\x{27}\x{2f}\x{3a}\x{3c}\x{3e}\x{40}";

        $parts = explode("/", $jid, 2);
        if (count($parts) > 1) {
            $resource = $parts[1];
            if ($resource == '') {
                // Warning: empty resource isn't legit.
                // But if we're normalizing, we may as well take it...
            }
        } else {
            $resource = null;
        }

        $node = explode("@", $parts[0]);
        if ((count($node) > 2) || (count($node) == 0)) {
            // TRANS: Exception thrown when using too many @ signs in a Jabber ID.
            throw new Exception(_m('Invalid JID: too many @s.'));
        } else if (count($node) == 1) {
            $domain = $node[0];
            $node = null;
        } else {
            $domain = $node[1];
            $node = $node[0];
            if ($node == '') {
            // TRANS: Exception thrown when using @ sign not followed by a Jabber ID.
                throw new Exception(_m('Invalid JID: @ but no node'));
            }
        }

        // Length limits per http://xmpp.org/rfcs/rfc3920.html#addressing
        if ($node !== null) {
            if (strlen($node) > 1023) {
                // TRANS: Exception thrown when using too long a Jabber ID (>1023).
                throw new Exception(_m('Invalid JID: node too long.'));
            }
            if (preg_match("/[".$nodeprepchars."]/u", $node)) {
                // TRANS: Exception thrown when using an invalid Jabber ID.
                // TRANS: %s is the invalid Jabber ID.
                throw new Exception(sprintf(_m('Invalid JID node "%s".'),$node));
            }
        }

        if (strlen($domain) > 1023) {
            // TRANS: Exception thrown when using too long a Jabber domain (>1023).
            throw new Exception(_m('Invalid JID: domain too long.'));
        }
        if (!common_valid_domain($domain)) {
            // TRANS: Exception thrown when using an invalid Jabber domain name.
            // TRANS: %s is the invalid domain name.
            throw new Exception(sprintf(_m('Invalid JID domain name "%s".'),$domain));
        }

        if ($resource !== null) {
            if (strlen($resource) > 1023) {
                // TRANS: Exception thrown when using too long a resource (>1023).
                throw new Exception("Invalid JID: resource too long.");
            }
            if (preg_match("/[".$chars."]/u", $resource)) {
                // TRANS: Exception thrown when using an invalid Jabber resource.
                // TRANS: %s is the invalid resource.
                throw new Exception(sprintf(_m('Invalid JID resource "%s".'),$resource));
            }
        }

        return array('node' => is_null($node) ? null : mb_strtolower($node),
                     'domain' => is_null($domain) ? null : mb_strtolower($domain),
                     'resource' => $resource);
    }

    /**
     * Checks whether a string is a syntactically valid Jabber ID (JID),
     * either with or without a resource.
     *
     * Note that a bare domain can be a valid JID.
     *
     * @param string $jid string to check
     * @param bool $check_domain whether we should validate that domain...
     *
     * @return     boolean whether the string is a valid JID
     */
    protected function validateFullJid($jid, $check_domain=false)
    {
        try {
            $parts = $this->splitJid($jid);
            if ($check_domain) {
                if (!$this->checkDomain($parts['domain'])) {
                    return false;
                }
            }
            return $parts['resource'] !== ''; // missing or present; empty ain't kosher
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Checks whether a string is a syntactically valid base Jabber ID (JID).
     * A base JID won't include a resource specifier on the end; since we
     * take it off when reading input we can't really use them reliably
     * to direct outgoing messages yet (sorry guys!)
     *
     * Note that a bare domain can be a valid JID.
     *
     * @param string $jid string to check
     * @param bool $check_domain whether we should validate that domain...
     *
     * @return     boolean whether the string is a valid JID
     */
    protected function validateBaseJid($jid, $check_domain=false)
    {
        try {
            $parts = $this->splitJid($jid);
            if ($check_domain) {
                if (!$this->checkDomain($parts['domain'])) {
                    return false;
                }
            }
            return ($parts['resource'] === null); // missing; empty ain't kosher
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Normalizes a Jabber ID for comparison, dropping the resource component if any.
     *
     * @param string $jid JID to check
     * @param bool $check_domain if true, reject if the domain isn't findable
     *
     * @return string an equivalent JID in normalized (lowercase) form
     */
    function normalize($jid)
    {
        try {
            $parts = $this->splitJid($jid);
            if ($parts['node'] !== null) {
                return $parts['node'] . '@' . $parts['domain'];
            } else {
                return $parts['domain'];
            }
        } catch (Exception $e) {
            return null;
        }
    }

    /**
     * Check if this domain's got some legit DNS record
     */
    protected function checkDomain($domain)
    {
        if (checkdnsrr("_xmpp-server._tcp." . $domain, "SRV")) {
            return true;
        }
        if (checkdnsrr($domain, "ANY")) {
            return true;
        }
        return false;
    }

    function daemonScreenname()
    {
        $ret = $this->user . '@' . $this->server;
        if($this->resource)
        {
            return $ret . '/' . $this->resource;
        }else{
            return $ret;
        }
    }

    function validate($screenname)
    {
        return $this->validateBaseJid($screenname, common_config('email', 'check_domain'));
    }

    /**
     * Load related modules when needed
     *
     * @param string $cls Name of the class to be loaded
     *
     * @return boolean hook value; true means continue processing, false means stop.
     */

    function onAutoload($cls)
    {
        $dir = dirname(__FILE__);

        switch ($cls)
        {
        case 'XMPPHP_XMPP':
            require_once $dir . '/extlib/XMPPHP/XMPP.php';
            return false;
        }

        return parent::onAutoload($cls);
    }

    function onStartImDaemonIoManagers(&$classes)
    {
        parent::onStartImDaemonIoManagers($classes);
        $classes[] = new XmppManager($this); // handles pings/reconnects
        return true;
    }

    function sendMessage($screenname, $body)
    {
        $this->queuedConnection()->message($screenname, $body, 'chat');
    }

    function sendNotice($screenname, Notice $notice)
    {
        try {
            $msg   = $this->formatNotice($notice);
            $entry = $this->format_entry($notice);
        } catch (Exception $e) {
            common_log(LOG_ERR, __METHOD__ . ": Discarding outgoing stanza because of exception: {$e->getMessage()}");
            return false;   // return value of sendNotice is never actually used as of now
        }
        $this->queuedConnection()->message($screenname, $msg, 'chat', null, $entry);
        return true;
    }

    /**
     * extra information for XMPP messages, as defined by Twitter
     *
     * @param Profile $profile Profile of the sending user
     * @param Notice  $notice  Notice being sent
     *
     * @return string Extra information (Atom, HTML, addresses) in string format
     */
    protected function format_entry(Notice $notice)
    {
        $profile = $notice->getProfile();

        $entry = $notice->asAtomEntry(true, true);

        $xs = new XMLStringer();
        $xs->elementStart('html', array('xmlns' => 'http://jabber.org/protocol/xhtml-im'));
        $xs->elementStart('body', array('xmlns' => 'http://www.w3.org/1999/xhtml'));
        $xs->element('a', array('href' => $profile->profileurl), $profile->nickname);
        try {
            $parent = $notice->getParent();
            $orig_profile = $parent->getProfile();
            $orig_profurl = $orig_profile->getUrl();
            $xs->text(" => ");
            $xs->element('a', array('href' => $orig_profurl), $orig_profile->nickname);
            $xs->text(": ");
        } catch (InvalidUrlException $e) {
            $xs->text(sprintf(' => %s', $orig_profile->nickname));
        } catch (NoParentNoticeException $e) {
            $xs->text(": ");
        } catch (NoResultException $e) {
            // Parent notice was probably deleted.
            $xs->text(": ");
        }
        // FIXME: Why do we replace \t with ''? is it just to make it pretty? shouldn't whitespace be handled well...?
        $xs->raw(str_replace("\t", "", $notice->getRendered()));
        $xs->text(" ");
        $xs->element('a', array(
            'href'=>common_local_url('conversation',
                array('id' => $notice->conversation)).'#notice-'.$notice->id),
             // TRANS: Link description to notice in conversation.
             // TRANS: %s is a notice ID.
             sprintf(_m('[%u]'),$notice->id));
        $xs->elementEnd('body');
        $xs->elementEnd('html');

        $html = $xs->getString();

        return $html . ' ' . $entry;
    }

    function receiveRawMessage($pl)
    {
        $from = $this->normalize($pl['from']);

        if ($pl['type'] != 'chat') {
            $this->log(LOG_WARNING, "Ignoring message of type ".$pl['type']." from $from: " . $pl['xml']->toString());
            return true;
        }

        if (mb_strlen($pl['body']) == 0) {
            $this->log(LOG_WARNING, "Ignoring message with empty body from $from: "  . $pl['xml']->toString());
            return true;
        }

        $this->handleIncoming($from, $pl['body']);

        return true;
    }

    /**
     * Build a queue-proxied XMPP interface object. Any outgoing messages
     * will be run back through us for enqueing rather than sent directly.
     *
     * @return QueuedXMPP
     * @throws Exception if server settings are invalid.
     */
    function queuedConnection(){
        if(!isset($this->server)){
            // TRANS: Exception thrown when the plugin configuration is incorrect.
            throw new Exception(_m('You must specify a server in the configuration.'));
        }
        if(!isset($this->port)){
            // TRANS: Exception thrown when the plugin configuration is incorrect.
            throw new Exception(_m('You must specify a port in the configuration.'));
        }
        if(!isset($this->user)){
            // TRANS: Exception thrown when the plugin configuration is incorrect.
            throw new Exception(_m('You must specify a user in the configuration.'));
        }
        if(!isset($this->password)){
            // TRANS: Exception thrown when the plugin configuration is incorrect.
            throw new Exception(_m('You must specify a password in the configuration.'));
        }

        return new QueuedXMPP($this, $this->host ?
                                    $this->host :
                                    $this->server,
                                    $this->port,
                                    $this->user,
                                    $this->password,
                                    $this->resource,
                                    $this->server,
                                    $this->debug ?
                                    true : false,
                                    $this->debug ?
                                    XMPPHP_Log::LEVEL_VERBOSE :  null
                                    );
    }

    /**
     * Add XMPP plugin daemon to the list of daemon to start
     *
     * @param array $daemons the list of daemons to run
     *
     * @return boolean hook return
     */
    function onGetValidDaemons(&$daemons)
    {
        if( isset($this->server) &&
            isset($this->port)   &&
            isset($this->user)   &&
            isset($this->password) ){

            array_push(
                $daemons,
                INSTALLDIR
                . '/scripts/imdaemon.php'
            );
        }

        return true;
    }

    function onPluginVersion(array &$versions)
    {
        $versions[] = array('name' => 'XMPP',
                            'version' => GNUSOCIAL_VERSION,
                            'author' => 'Craig Andrews, Evan Prodromou',
                            'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/XMPP',
                            'rawdescription' =>
                            // TRANS: Plugin description.
                            _m('The XMPP plugin allows users to send and receive notices over the XMPP/Jabber network.'));
        return true;
    }
}


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/** XMPPHP_XMLStream */
require_once dirname(__FILE__) . "/XMPP.php";

/**
 * XMPPHP Main Class
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 * @version	$Id$
 */
class XMPPHP_BOSH extends XMPPHP_XMPP {

		protected $rid;
		protected $sid;
		protected $http_server;
		protected $http_buffer = Array();
		protected $session = false;

		public function connect($server, $wait='1', $session=false) {
			$this->http_server = $server;
			$this->use_encryption = false;
			$this->session = $session;

			$this->rid = 3001;
			$this->sid = null;
			if($session)
			{
				$this->loadSession();
			}
			if(!$this->sid) {
				$body = $this->__buildBody();
				$body->addAttribute('hold','1');
				$body->addAttribute('to', $this->host);
				$body->addAttribute('route', "xmpp:{$this->host}:{$this->port}");
				$body->addAttribute('secure','true');
				$body->addAttribute('xmpp:version','1.6', 'urn:xmpp:xbosh');
				$body->addAttribute('wait', strval($wait));
				$body->addAttribute('ack','1');
				$body->addAttribute('xmlns:xmpp','urn:xmpp:xbosh');
				$buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
				xml_parse($this->parser, $buff, false);
				$response = $this->__sendBody($body);
				$rxml = new SimpleXMLElement($response);
				$this->sid = $rxml['sid'];

			} else {
				$buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
				xml_parse($this->parser, $buff, false);
			}
		}

		public function __sendBody($body=null, $recv=true) {
			if(!$body) {
				$body = $this->__buildBody();
			}
			$ch = curl_init($this->http_server);
			curl_setopt($ch, CURLOPT_HEADER, 0);
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $body->asXML());
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
			$header = array('Accept-Encoding: gzip, deflate','Content-Type: text/xml; charset=utf-8');
			curl_setopt($ch, CURLOPT_HTTPHEADER, $header );
			curl_setopt($ch, CURLOPT_VERBOSE, 0);
			$output = '';
			if($recv) {
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
				$output = curl_exec($ch);
				$this->http_buffer[] = $output;
			}
			curl_close($ch);
			return $output;
		}

		public function __buildBody($sub=null) {
			$xml = new SimpleXMLElement("<body xmlns='http://jabber.org/protocol/httpbind' xmlns:xmpp='urn:xmpp:xbosh' />");
			$xml->addAttribute('content', 'text/xml; charset=utf-8');
			$xml->addAttribute('rid', $this->rid);
			$this->rid += 1;
			if($this->sid) $xml->addAttribute('sid', $this->sid);
			#if($this->sid) $xml->addAttribute('xmlns', 'http://jabber.org/protocol/httpbind');
			$xml->addAttribute('xml:lang', 'en');
			if($sub) { // ok, so simplexml is lame
				$p = dom_import_simplexml($xml);
				$c = dom_import_simplexml($sub);
				$cn = $p->ownerDocument->importNode($c, true);
				$p->appendChild($cn);
				$xml = simplexml_import_dom($p);
			}
			return $xml;
		}

		public function __process() {
			if($this->http_buffer) {
				$this->__parseBuffer();
			} else {
				$this->__sendBody();
				$this->__parseBuffer();
			}
		}

		public function __parseBuffer() {
			while ($this->http_buffer) {
				$idx = key($this->http_buffer);
				$buffer = $this->http_buffer[$idx];
				unset($this->http_buffer[$idx]);
				if($buffer) {
					$xml = new SimpleXMLElement($buffer);
					$children = $xml->xpath('child::node()');
					foreach ($children as $child) {
						$buff = $child->asXML();
						$this->log->log("RECV: $buff",  XMPPHP_Log::LEVEL_VERBOSE);
						xml_parse($this->parser, $buff, false);
					}
				}
			}
		}

		public function send($msg) {
			$this->log->log("SEND: $msg",  XMPPHP_Log::LEVEL_VERBOSE);
			$msg = new SimpleXMLElement($msg);
			#$msg->addAttribute('xmlns', 'jabber:client');
			$this->__sendBody($this->__buildBody($msg), true);
			#$this->__parseBuffer();
		}

		public function reset() {
			$this->xml_depth = 0;
			unset($this->xmlobj);
			$this->xmlobj = array();
			$this->setupParser();
			#$this->send($this->stream_start);
			$body = $this->__buildBody();
			$body->addAttribute('to', $this->host);
			$body->addAttribute('xmpp:restart', 'true', 'urn:xmpp:xbosh');
			$buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
			$response = $this->__sendBody($body);
			$this->been_reset = true;
			xml_parse($this->parser, $buff, false);
		}

		public function loadSession() {
			if(isset($_SESSION['XMPPHP_BOSH_RID'])) $this->rid = $_SESSION['XMPPHP_BOSH_RID'];
			if(isset($_SESSION['XMPPHP_BOSH_SID'])) $this->sid = $_SESSION['XMPPHP_BOSH_SID'];
			if(isset($_SESSION['XMPPHP_BOSH_authed'])) $this->authed = $_SESSION['XMPPHP_BOSH_authed'];
			if(isset($_SESSION['XMPPHP_BOSH_jid'])) $this->jid = $_SESSION['XMPPHP_BOSH_jid'];
			if(isset($_SESSION['XMPPHP_BOSH_fulljid'])) $this->fulljid = $_SESSION['XMPPHP_BOSH_fulljid'];
		}

		public function saveSession() {
			$_SESSION['XMPPHP_BOSH_RID'] = (string) $this->rid;
			$_SESSION['XMPPHP_BOSH_SID'] = (string) $this->sid;
			$_SESSION['XMPPHP_BOSH_authed'] = (boolean) $this->authed;
			$_SESSION['XMPPHP_BOSH_jid'] = (string) $this->jid;
			$_SESSION['XMPPHP_BOSH_fulljid'] = (string) $this->fulljid;
		}
}


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package    XMPPHP
 * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/**
 * XMPPHP Exception
 *
 * @category   xmpphp
 * @package    XMPPHP
 * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 * @version    $Id$
 */
class XMPPHP_Exception extends Exception {
}


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/**
 * XMPPHP Log
 *
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 * @version	$Id$
 */
class XMPPHP_Log {

	const LEVEL_ERROR   = 0;
	const LEVEL_WARNING = 1;
	const LEVEL_INFO	= 2;
	const LEVEL_DEBUG   = 3;
	const LEVEL_VERBOSE = 4;

	/**
	 * @var array
	 */
	protected $data = array();

	/**
	 * @var array
	 */
	protected $names = array('ERROR', 'WARNING', 'INFO', 'DEBUG', 'VERBOSE');

	/**
	 * @var integer
	 */
	protected $runlevel;

	/**
	 * @var boolean
	 */
	protected $printout;

	/**
	 * Constructor
	 *
	 * @param boolean $printout
	 * @param string  $runlevel
	 */
	public function __construct($printout = false, $runlevel = self::LEVEL_INFO) {
		$this->printout = (boolean)$printout;
		$this->runlevel = (int)$runlevel;
	}

	/**
	 * Add a message to the log data array
	 * If printout in this instance is set to true, directly output the message
	 *
	 * @param string  $msg
	 * @param integer $runlevel
	 */
	public function log($msg, $runlevel = self::LEVEL_INFO) {
		$time = time();
		#$this->data[] = array($this->runlevel, $msg, $time);
		if($this->printout and $runlevel <= $this->runlevel) {
			$this->writeLine($msg, $runlevel, $time);
		}
	}

	/**
	 * Output the complete log.
	 * Log will be cleared if $clear = true
	 *
	 * @param boolean $clear
	 * @param integer $runlevel
	 */
	public function printout($clear = true, $runlevel = null) {
		if($runlevel === null) {
			$runlevel = $this->runlevel;
		}
		foreach($this->data as $data) {
			if($runlevel <= $data[0]) {
				$this->writeLine($data[1], $runlevel, $data[2]);
			}
		}
		if($clear) {
			$this->data = array();
		}
	}

	protected function writeLine($msg, $runlevel, $time) {
		//echo date('Y-m-d H:i:s', $time)." [".$this->names[$runlevel]."]: ".$msg."\n";
		echo $time." [".$this->names[$runlevel]."]: ".$msg."\n";
		flush();
	}
}


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/**
 * XMPPHP Roster Object
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 * @version	$Id$
 */

class Roster {
	/**
	 * Roster array, handles contacts and presence.  Indexed by jid.
	 * Contains array with potentially two indexes 'contact' and 'presence'
	 * @var array
	 */
	protected $roster_array = array();
	/**
	 * Constructor
	 *
	 */
	public function __construct($roster_array = array()) {
		if ($this->verifyRoster($roster_array)) {
			$this->roster_array = $roster_array; //Allow for prepopulation with existing roster
		} else {
			$this->roster_array = array();
		}
	}

	/**
	 *
	 * Check that a given roster array is of a valid structure (empty is still valid)
	 *
	 * @param array $roster_array
	 */
	protected function verifyRoster($roster_array) {
		#TODO once we know *what* a valid roster array looks like
		return True;
	}

	/**
	 *
	 * Add given contact to roster
	 *
	 * @param string $jid
	 * @param string $subscription
	 * @param string $name
	 * @param array $groups
	 */
	public function addContact($jid, $subscription, $name='', $groups=array()) {
		$contact = array('jid' => $jid, 'subscription' => $subscription, 'name' => $name, 'groups' => $groups);
		if ($this->isContact($jid)) {
			$this->roster_array[$jid]['contact'] = $contact;
		} else {
			$this->roster_array[$jid] = array('contact' => $contact);
		}
	}

	/**
	 *
	 * Retrieve contact via jid
	 *
	 * @param string $jid
	 */
	public function getContact($jid) {
		if ($this->isContact($jid)) {
			return $this->roster_array[$jid]['contact'];
		}
	}

	/**
	 *
	 * Discover if a contact exists in the roster via jid
	 *
	 * @param string $jid
	 */
	public function isContact($jid) {
		return (array_key_exists($jid, $this->roster_array));
	}

	/**
	 *
	 * Set presence
	 *
	 * @param string $presence
	 * @param integer $priority
	 * @param string $show
	 * @param string $status
	*/
	public function setPresence($presence, $priority, $show, $status) {
		$parts = explode('/', $presence);
		$jid = $parts[0];
		$resource = isset($parts[1]) ? $parts[1] : '';  // apparently we can do '' as an associative array index
		if ($show != 'unavailable') {
			if (!$this->isContact($jid)) {
				$this->addContact($jid, 'not-in-roster');
			}
			$this->roster_array[$jid]['presence'][$resource] = array('priority' => $priority, 'show' => $show, 'status' => $status);
		} else { //Nuke unavailable resources to save memory
			unset($this->roster_array[$jid]['resource'][$resource]);
		}
	}

	/*
	 *
	 * Return best presence for jid
	 *
	 * @param string $jid
	 */
	public function getPresence($jid) {
		$split = explode("/", $jid);
		$jid = $split[0];
		if($this->isContact($jid)) {
			$current = array('resource' => '', 'active' => '', 'priority' => -129, 'show' => '', 'status' => ''); //Priorities can only be -128 = 127
			foreach($this->roster_array[$jid]['presence'] as $resource => $presence) {
				//Highest available priority or just highest priority
				if ($presence['priority'] > $current['priority'] and (($presence['show'] == "chat" or $presence['show'] == "available") or ($current['show'] != "chat" or $current['show'] != "available"))) {
					$current = $presence;
					$current['resource'] = $resource;
				}
			}
			return $current;
		}
	}
	/**
	 *
	 * Get roster
	 *
	 */
	public function getRoster() {
		return $this->roster_array;
	}
}
?>


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/**
 * XMPPHP XML Object
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 * @version	$Id$
 */
class XMPPHP_XMLObj {
	/**
	 * Tag name
	 *
	 * @var string
	 */
	public $name;

	/**
	 * Namespace
	 *
	 * @var string
	 */
	public $ns;

	/**
	 * Attributes
	 *
	 * @var array
	 */
	public $attrs = array();

	/**
	 * Subs?
	 *
	 * @var array
	 */
	public $subs = array();

	/**
	 * Node data
	 *
	 * @var string
	 */
	public $data = '';

	/**
	 * Constructor
	 *
	 * @param string $name
	 * @param string $ns
	 * @param array  $attrs
	 * @param string $data
	 */
	public function __construct($name, $ns = '', $attrs = array(), $data = '') {
		$this->name = strtolower($name);
		$this->ns   = $ns;
		if(is_array($attrs) && count($attrs)) {
			foreach($attrs as $key => $value) {
				$this->attrs[strtolower($key)] = $value;
			}
		}
		$this->data = $data;
	}

	/**
	 * Dump this XML Object to output.
	 *
	 * @param integer $depth
	 */
	public function printObj($depth = 0) {
		print str_repeat("\t", $depth) . $this->name . " " . $this->ns . ' ' . $this->data;
		print "\n";
		foreach($this->subs as $sub) {
			$sub->printObj($depth + 1);
		}
	}

	/**
	 * Return this XML Object in xml notation
	 *
	 * @param string $str
	 */
	public function toString($str = '') {
		$str .= "<{$this->name} xmlns='{$this->ns}' ";
		foreach($this->attrs as $key => $value) {
			if($key != 'xmlns') {
				$value = htmlspecialchars($value);
				$str .= "$key='$value' ";
			}
		}
		$str .= ">";
		foreach($this->subs as $sub) {
			$str .= $sub->toString();
		}
		$body = htmlspecialchars($this->data);
		$str .= "$body</{$this->name}>";
		return $str;
	}

	/**
	 * Has this XML Object the given sub?
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function hasSub($name, $ns = null) {
		foreach($this->subs as $sub) {
			if(($name == "*" or $sub->name == $name) and ($ns == null or $sub->ns == $ns)) return true;
		}
		return false;
	}

	/**
	 * Return a sub
	 *
	 * @param string $name
	 * @param string $attrs
	 * @param string $ns
	 */
	public function sub($name, $attrs = null, $ns = null) {
		#TODO attrs is ignored
		foreach($this->subs as $sub) {
			if($sub->name == $name and ($ns == null or $sub->ns == $ns)) {
				return $sub;
			}
		}
	}
}


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/** XMPPHP_Exception */
require_once dirname(__FILE__) . '/Exception.php';

/** XMPPHP_XMLObj */
require_once dirname(__FILE__) . '/XMLObj.php';

/** XMPPHP_Log */
require_once dirname(__FILE__) . '/Log.php';

/**
 * XMPPHP XML Stream
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 * @version	$Id$
 */
class XMPPHP_XMLStream {
	/**
	 * @var resource
	 */
	protected $socket;
	/**
	 * @var resource
	 */
	protected $parser;
	/**
	 * @var string
	 */
	protected $buffer;
	/**
	 * @var integer
	 */
	protected $xml_depth = 0;
	/**
	 * @var string
	 */
	protected $host;
	/**
	 * @var integer
	 */
	protected $port;
	/**
	 * @var string
	 */
	protected $stream_start = '<stream>';
	/**
	 * @var string
	 */
	protected $stream_end = '</stream>';
	/**
	 * @var boolean
	 */
	protected $disconnected = false;
	/**
	 * @var boolean
	 */
	protected $sent_disconnect = false;
	/**
	 * @var array
	 */
	protected $ns_map = array();
	/**
	 * @var array
	 */
	protected $current_ns = array();
	/**
	 * @var array
	 */
	protected $xmlobj = null;
	/**
	 * @var array
	 */
	protected $nshandlers = array();
	/**
	 * @var array
	 */
	protected $xpathhandlers = array();
	/**
	 * @var array
	 */
	protected $idhandlers = array();
	/**
	 * @var array
	 */
	protected $eventhandlers = array();
	/**
	 * @var integer
	 */
	protected $lastid = 0;
	/**
	 * @var string
	 */
	protected $default_ns;
	/**
	 * @var string
	 */
	protected $until = '';
	/**
	 * @var string
	 */
	protected $until_count = '';
	/**
	 * @var array
	 */
	protected $until_happened = false;
	/**
	 * @var array
	 */
	protected $until_payload = array();
	/**
	 * @var XMPPHP_Log
	 */
	protected $log;
	/**
	 * @var boolean
	 */
	protected $reconnect = true;
	/**
	 * @var boolean
	 */
	protected $been_reset = false;
	/**
	 * @var boolean
	 */
	protected $is_server;
	/**
	 * @var float
	 */
	protected $last_send = 0;
	/**
	 * @var boolean
	 */
	protected $use_ssl = false;
	/**
	 * @var integer
	 */
	protected $reconnectTimeout = 30;

	/**
	 * Constructor
	 *
	 * @param string  $host
	 * @param string  $port
	 * @param boolean $printlog
	 * @param string  $loglevel
	 * @param boolean $is_server
	 */
	public function __construct($host = null, $port = null, $printlog = false, $loglevel = null, $is_server = false) {
		$this->reconnect = !$is_server;
		$this->is_server = $is_server;
		$this->host = $host;
		$this->port = $port;
		$this->setupParser();
		$this->log = new XMPPHP_Log($printlog, $loglevel);
	}

	/**
	 * Destructor
	 * Cleanup connection
	 */
	public function __destruct() {
		if(!$this->disconnected && $this->socket) {
			$this->disconnect();
		}
	}

	/**
	 * Return the log instance
	 *
	 * @return XMPPHP_Log
	 */
	public function getLog() {
		return $this->log;
	}

	/**
	 * Get next ID
	 *
	 * @return integer
	 */
	public function getId() {
		$this->lastid++;
		return $this->lastid;
	}

	/**
	 * Set SSL
	 *
	 * @return integer
	 */
	public function useSSL($use=true) {
		$this->use_ssl = $use;
	}

	/**
	 * Add ID Handler
	 *
	 * @param integer $id
	 * @param string  $pointer
	 * @param string  $obj
	 */
	public function addIdHandler($id, $pointer, $obj = null) {
		$this->idhandlers[$id] = array($pointer, $obj);
	}

	/**
	 * Add Handler
	 *
	 * @param string $name
	 * @param string  $ns
	 * @param string  $pointer
	 * @param string  $obj
	 * @param integer $depth
	 */
	public function addHandler($name, $ns, $pointer, $obj = null, $depth = 1) {
		#TODO deprication warning
		$this->nshandlers[] = array($name,$ns,$pointer,$obj, $depth);
	}

	/**
	 * Add XPath Handler
	 *
	 * @param string $xpath
	 * @param string $pointer
	 * @param
	 */
	public function addXPathHandler($xpath, $pointer, $obj = null) {
		if (preg_match_all("/\(?{[^\}]+}\)?(\/?)[^\/]+/", $xpath, $regs)) {
			$ns_tags = $regs[0];
		} else {
			$ns_tags = array($xpath);
		}
		foreach($ns_tags as $ns_tag) {
			list($l, $r) = explode("}", $ns_tag);
			if ($r != null) {
				$xpart = array(substr($l, 1), $r);
			} else {
				$xpart = array(null, $l);
			}
			$xpath_array[] = $xpart;
		}
		$this->xpathhandlers[] = array($xpath_array, $pointer, $obj);
	}

	/**
	 * Add Event Handler
	 *
	 * @param integer $id
	 * @param string  $pointer
	 * @param string  $obj
	 */
	public function addEventHandler($name, $pointer, $obj) {
		$this->eventhandlers[] = array($name, $pointer, $obj);
	}

	/**
	 * Connect to XMPP Host
	 *
	 * @param integer $timeout
	 * @param boolean $persistent
	 * @param boolean $sendinit
	 */
	public function connect($timeout = 30, $persistent = false, $sendinit = true) {
		$this->sent_disconnect = false;
		$starttime = time();

		do {
			$this->disconnected = false;
			$this->sent_disconnect = false;
			if($persistent) {
				$conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
			} else {
				$conflag = STREAM_CLIENT_CONNECT;
			}
			$conntype = 'tcp';
			if($this->use_ssl) $conntype = 'ssl';
			$this->log->log("Connecting to $conntype://{$this->host}:{$this->port}");
			try {
				$this->socket = @stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag);
			} catch (Exception $e) {
				throw new XMPPHP_Exception($e->getMessage());
			}
			if(!$this->socket) {
				$this->log->log("Could not connect.",  XMPPHP_Log::LEVEL_ERROR);
				$this->disconnected = true;
				# Take it easy for a few seconds
				sleep(min($timeout, 5));
			}
		} while (!$this->socket && (time() - $starttime) < $timeout);

		if ($this->socket) {
			stream_set_blocking($this->socket, 1);
			if($sendinit) $this->send($this->stream_start);
		} else {
			throw new XMPPHP_Exception("Could not connect before timeout.");
		}
	}

	/**
	 * Reconnect XMPP Host
	 */
	public function doReconnect() {
		if(!$this->is_server) {
			$this->log->log("Reconnecting ($this->reconnectTimeout)...",  XMPPHP_Log::LEVEL_WARNING);
			$this->connect($this->reconnectTimeout, false, false);
			$this->reset();
			$this->event('reconnect');
		}
	}

	public function setReconnectTimeout($timeout) {
		$this->reconnectTimeout = $timeout;
	}

	/**
	 * Disconnect from XMPP Host
	 */
	public function disconnect() {
		$this->log->log("Disconnecting...",  XMPPHP_Log::LEVEL_VERBOSE);
		if(false == (bool) $this->socket) {
			return;
		}
		$this->reconnect = false;
		$this->send($this->stream_end);
		$this->sent_disconnect = true;
		$this->processUntil('end_stream', 5);
		$this->disconnected = true;
	}

	/**
	 * Are we are disconnected?
	 *
	 * @return boolean
	 */
	public function isDisconnected() {
		return $this->disconnected;
	}

	/**
	 * Core reading tool
	 * 0 -> only read if data is immediately ready
	 * NULL -> wait forever and ever
	 * integer -> process for this amount of time
	 */

	private function __process($maximum=5) {

		$remaining = $maximum;

		do {
			$starttime = (microtime(true) * 1000000);
			$read = array($this->socket);
			$write = array();
			$except = array();
			if (is_null($maximum)) {
				$secs = NULL;
				$usecs = NULL;
			} else if ($maximum == 0) {
				$secs = 0;
				$usecs = 0;
			} else {
				$usecs = $remaining % 1000000;
				$secs = floor(($remaining - $usecs) / 1000000);
			}
			$updated = @stream_select($read, $write, $except, $secs, $usecs);
			if ($updated === false) {
				$this->log->log("Error on stream_select()",  XMPPHP_Log::LEVEL_VERBOSE);
				if ($this->reconnect) {
					$this->doReconnect();
				} else {
					fclose($this->socket);
					$this->socket = NULL;
					return false;
				}
			} else if ($updated > 0) {
				# XXX: Is this big enough?
				$buff = @fread($this->socket, 4096);
				if(!$buff) {
					if($this->reconnect) {
						$this->doReconnect();
					} else {
						fclose($this->socket);
						$this->socket = NULL;
						return false;
					}
				}
				$this->log->log("RECV: $buff",  XMPPHP_Log::LEVEL_VERBOSE);
				xml_parse($this->parser, $buff, false);
			} else {
				# $updated == 0 means no changes during timeout.
			}
			$endtime = (microtime(true)*1000000);
			$time_past = $endtime - $starttime;
			$remaining = $remaining - $time_past;
		} while (is_null($maximum) || $remaining > 0);
		return true;
	}

	/**
	 * Process
	 *
	 * @return string
	 */
	public function process() {
		$this->__process(NULL);
	}

	/**
	 * Process until a timeout occurs
	 *
	 * @param integer $timeout
	 * @return string
	 */
	public function processTime($timeout=NULL) {
		if (is_null($timeout)) {
			return $this->__process(NULL);
		} else {
			return $this->__process($timeout * 1000000);
		}
	}

	/**
	 * Process until a specified event or a timeout occurs
	 *
	 * @param string|array $event
	 * @param integer $timeout
	 * @return string
	 */
	public function processUntil($event, $timeout=-1) {
		$start = time();
		if(!is_array($event)) $event = array($event);
		$this->until[] = $event;
		end($this->until);
		$event_key = key($this->until);
		reset($this->until);
		$this->until_count[$event_key] = 0;
		$updated = '';
		while(!$this->disconnected and $this->until_count[$event_key] < 1 and (time() - $start < $timeout or $timeout == -1)) {
			$this->__process();
		}
		if(array_key_exists($event_key, $this->until_payload)) {
			$payload = $this->until_payload[$event_key];
			unset($this->until_payload[$event_key]);
			unset($this->until_count[$event_key]);
			unset($this->until[$event_key]);
		} else {
			$payload = array();
		}
		return $payload;
	}

	/**
	 * Obsolete?
	 */
	public function Xapply_socket($socket) {
		$this->socket = $socket;
	}

	/**
	 * XML start callback
	 *
	 * @see xml_set_element_handler
	 *
	 * @param resource $parser
	 * @param string   $name
	 */
	public function startXML($parser, $name, $attr) {
		if($this->been_reset) {
			$this->been_reset = false;
			$this->xml_depth = 0;
		}
		$this->xml_depth++;
		if(array_key_exists('XMLNS', $attr)) {
			$this->current_ns[$this->xml_depth] = $attr['XMLNS'];
		} else {
			$this->current_ns[$this->xml_depth] = $this->current_ns[$this->xml_depth - 1];
			if(!$this->current_ns[$this->xml_depth]) $this->current_ns[$this->xml_depth] = $this->default_ns;
		}
		$ns = $this->current_ns[$this->xml_depth];
		foreach($attr as $key => $value) {
			if(strstr($key, ":")) {
				$key = explode(':', $key);
				$key = $key[1];
				$this->ns_map[$key] = $value;
			}
		}
		if(!strstr($name, ":") === false)
		{
			$name = explode(':', $name);
			$ns = $this->ns_map[$name[0]];
			$name = $name[1];
		}
		$obj = new XMPPHP_XMLObj($name, $ns, $attr);
		if($this->xml_depth > 1) {
			$this->xmlobj[$this->xml_depth - 1]->subs[] = $obj;
		}
		$this->xmlobj[$this->xml_depth] = $obj;
	}

	/**
	 * XML end callback
	 *
	 * @see xml_set_element_handler
	 *
	 * @param resource $parser
	 * @param string   $name
	 */
	public function endXML($parser, $name) {
		#$this->log->log("Ending $name",  XMPPHP_Log::LEVEL_DEBUG);
		#print "$name\n";
		if($this->been_reset) {
			$this->been_reset = false;
			$this->xml_depth = 0;
		}
		$this->xml_depth--;
		if($this->xml_depth == 1) {
			#clean-up old objects
			#$found = false; #FIXME This didn't appear to be in use --Gar
			foreach($this->xpathhandlers as $handler) {
				if (is_array($this->xmlobj) && array_key_exists(2, $this->xmlobj)) {
					$searchxml = $this->xmlobj[2];
					$nstag = array_shift($handler[0]);
					if (($nstag[0] == null or $searchxml->ns == $nstag[0]) and ($nstag[1] == "*" or $nstag[1] == $searchxml->name)) {
						foreach($handler[0] as $nstag) {
							if ($searchxml !== null and $searchxml->hasSub($nstag[1], $ns=$nstag[0])) {
								$searchxml = $searchxml->sub($nstag[1], $ns=$nstag[0]);
							} else {
								$searchxml = null;
								break;
							}
						}
						if ($searchxml !== null) {
							if($handler[2] === null) $handler[2] = $this;
							$this->log->log("Calling {$handler[1]}",  XMPPHP_Log::LEVEL_DEBUG);
							$handler[2]->{$handler[1]}($this->xmlobj[2]);
						}
					}
				}
			}
			foreach($this->nshandlers as $handler) {
				if($handler[4] != 1 and array_key_exists(2, $this->xmlobj) and  $this->xmlobj[2]->hasSub($handler[0])) {
					$searchxml = $this->xmlobj[2]->sub($handler[0]);
				} elseif(is_array($this->xmlobj) and array_key_exists(2, $this->xmlobj)) {
					$searchxml = $this->xmlobj[2];
				}
				if($searchxml !== null and $searchxml->name == $handler[0] and ($searchxml->ns == $handler[1] or (!$handler[1] and $searchxml->ns == $this->default_ns))) {
					if($handler[3] === null) $handler[3] = $this;
					$this->log->log("Calling {$handler[2]}",  XMPPHP_Log::LEVEL_DEBUG);
					$handler[3]->{$handler[2]}($this->xmlobj[2]);
				}
			}
			foreach($this->idhandlers as $id => $handler) {
				if(array_key_exists('id', $this->xmlobj[2]->attrs) and $this->xmlobj[2]->attrs['id'] == $id) {
					if($handler[1] === null) $handler[1] = $this;
					$handler[1]->{$handler[0]}($this->xmlobj[2]);
					#id handlers are only used once
					unset($this->idhandlers[$id]);
					break;
				}
			}
			if(is_array($this->xmlobj)) {
				$this->xmlobj = array_slice($this->xmlobj, 0, 1);
				if(isset($this->xmlobj[0]) && $this->xmlobj[0] instanceof XMPPHP_XMLObj) {
					$this->xmlobj[0]->subs = null;
				}
			}
			unset($this->xmlobj[2]);
		}
		if($this->xml_depth == 0 and !$this->been_reset) {
			if(!$this->disconnected) {
				if(!$this->sent_disconnect) {
					$this->send($this->stream_end);
				}
				$this->disconnected = true;
				$this->sent_disconnect = true;
				fclose($this->socket);
				if($this->reconnect) {
					$this->doReconnect();
				}
			}
			$this->event('end_stream');
		}
	}

	/**
	 * XML character callback
	 * @see xml_set_character_data_handler
	 *
	 * @param resource $parser
	 * @param string   $data
	 */
	public function charXML($parser, $data) {
		if(array_key_exists($this->xml_depth, $this->xmlobj)) {
			$this->xmlobj[$this->xml_depth]->data .= $data;
		}
	}

	/**
	 * Event?
	 *
	 * @param string $name
	 * @param string $payload
	 */
	public function event($name, $payload = null) {
		$this->log->log("EVENT: $name",  XMPPHP_Log::LEVEL_DEBUG);
		foreach($this->eventhandlers as $handler) {
			if($name == $handler[0]) {
				if($handler[2] === null) {
					$handler[2] = $this;
				}
				$handler[2]->{$handler[1]}($payload);
			}
		}
		foreach($this->until as $key => $until) {
			if(is_array($until)) {
				if(in_array($name, $until)) {
					$this->until_payload[$key][] = array($name, $payload);
					if(!isset($this->until_count[$key])) {
						$this->until_count[$key] = 0;
					}
					$this->until_count[$key] += 1;
					#$this->until[$key] = false;
				}
			}
		}
	}

	/**
	 * Read from socket
	 */
	public function read() {
		$buff = @fread($this->socket, 1024);
		if(!$buff) {
			if($this->reconnect) {
				$this->doReconnect();
			} else {
				fclose($this->socket);
				return false;
			}
		}
		$this->log->log("RECV: $buff",  XMPPHP_Log::LEVEL_VERBOSE);
		xml_parse($this->parser, $buff, false);
	}

	/**
	 * Send to socket
	 *
	 * @param string $msg
	 */
	public function send($msg, $timeout=NULL) {

		if (is_null($timeout)) {
			$secs = NULL;
			$usecs = NULL;
		} else if ($timeout == 0) {
			$secs = 0;
			$usecs = 0;
		} else {
			$maximum = $timeout * 1000000;
			$usecs = $maximum % 1000000;
			$secs = floor(($maximum - $usecs) / 1000000);
		}

		$read = array();
		$write = array($this->socket);
		$except = array();

		$select = @stream_select($read, $write, $except, $secs, $usecs);

		if($select === False) {
			$this->log->log("ERROR sending message; reconnecting.");
			$this->doReconnect();
			# TODO: retry send here
			return false;
		} elseif ($select > 0) {
			$this->log->log("Socket is ready; send it.", XMPPHP_Log::LEVEL_VERBOSE);
		} else {
			$this->log->log("Socket is not ready; break.", XMPPHP_Log::LEVEL_ERROR);
			return false;
		}

		$sentbytes = @fwrite($this->socket, $msg);
		$this->log->log("SENT: " . mb_substr($msg, 0, $sentbytes, '8bit'), XMPPHP_Log::LEVEL_VERBOSE);
		if($sentbytes === FALSE) {
			$this->log->log("ERROR sending message; reconnecting.", XMPPHP_Log::LEVEL_ERROR);
			$this->doReconnect();
			return false;
		}
		$this->log->log("Successfully sent $sentbytes bytes.", XMPPHP_Log::LEVEL_VERBOSE);
		return $sentbytes;
	}

	public function time() {
		list($usec, $sec) = explode(" ", microtime());
		return (float)$sec + (float)$usec;
	}

	/**
	 * Reset connection
	 */
	public function reset() {
		$this->xml_depth = 0;
		unset($this->xmlobj);
		$this->xmlobj = array();
		$this->setupParser();
		if(!$this->is_server) {
			$this->send($this->stream_start);
		}
		$this->been_reset = true;
	}

	/**
	 * Setup the XML parser
	 */
	public function setupParser() {
		$this->parser = xml_parser_create('UTF-8');
		xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
		xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
		xml_set_object($this->parser, $this);
		xml_set_element_handler($this->parser, 'startXML', 'endXML');
		xml_set_character_data_handler($this->parser, 'charXML');
	}

	public function readyToProcess() {
		$read = array($this->socket);
		$write = array();
		$except = array();
		$updated = @stream_select($read, $write, $except, 0);
		return (($updated !== false) && ($updated > 0));
	}
}


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/** XMPPHP_XMLStream */
require_once dirname(__FILE__) . "/XMLStream.php";
require_once dirname(__FILE__) . "/Roster.php";

/**
 * XMPPHP Main Class
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 * @version	$Id$
 */
class XMPPHP_XMPP extends XMPPHP_XMLStream {
	/**
	 * @var string
	 */
	public $server;

	/**
	 * @var string
	 */
	public $user;

	/**
	 * @var string
	 */
	protected $password;

	/**
	 * @var string
	 */
	protected $resource;

	/**
	 * @var string
	 */
	protected $fulljid;

	/**
	 * @var string
	 */
	protected $basejid;

	/**
	 * @var boolean
	 */
	protected $authed = false;
	protected $session_started = false;

	/**
	 * @var boolean
	 */
	protected $auto_subscribe = false;

	/**
	 * @var boolean
	 */
	protected $use_encryption = true;

	/**
	 * @var boolean
	 */
	public $track_presence = true;

	/**
	 * @var object
	 */
	public $roster;

	/**
	 * Constructor
	 *
	 * @param string  $host
	 * @param integer $port
	 * @param string  $user
	 * @param string  $password
	 * @param string  $resource
	 * @param string  $server
	 * @param boolean $printlog
	 * @param string  $loglevel
	 */
	public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null) {
		parent::__construct($host, $port, $printlog, $loglevel);

		$this->user	 = $user;
		$this->password = $password;
		$this->resource = $resource;
		if(!$server) $server = $host;
		$this->basejid = $this->user . '@' . $this->host;

		$this->roster = new Roster();
		$this->track_presence = true;

		$this->stream_start = '<stream:stream to="' . $server . '" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0">';
		$this->stream_end   = '</stream:stream>';
		$this->default_ns   = 'jabber:client';

		$this->addXPathHandler('{http://etherx.jabber.org/streams}features', 'features_handler');
		$this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}success', 'sasl_success_handler');
		$this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}failure', 'sasl_failure_handler');
		$this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-tls}proceed', 'tls_proceed_handler');
		$this->addXPathHandler('{jabber:client}message', 'message_handler');
		$this->addXPathHandler('{jabber:client}presence', 'presence_handler');
		$this->addXPathHandler('iq/{jabber:iq:roster}query', 'roster_iq_handler');
	}

	/**
	 * Turn encryption on/ff
	 *
	 * @param boolean $useEncryption
	 */
	public function useEncryption($useEncryption = true) {
		$this->use_encryption = $useEncryption;
	}

	/**
	 * Turn on auto-authorization of subscription requests.
	 *
	 * @param boolean $autoSubscribe
	 */
	public function autoSubscribe($autoSubscribe = true) {
		$this->auto_subscribe = $autoSubscribe;
	}

	/**
	 * Send XMPP Message
	 *
	 * @param string $to
	 * @param string $body
	 * @param string $type
	 * @param string $subject
	 */
	public function message($to, $body, $type = 'chat', $subject = null, $payload = null) {
	    if(is_null($type))
	    {
	        $type = 'chat';
	    }

		$to	  = htmlspecialchars($to);
		$body	= htmlspecialchars($body);
		$subject = htmlspecialchars($subject);

		$out = "<message from=\"{$this->fulljid}\" to=\"$to\" type='$type'>";
		if($subject) $out .= "<subject>$subject</subject>";
		$out .= "<body>$body</body>";
		if($payload) $out .= $payload;
		$out .= "</message>";

		$this->send($out);
	}

	/**
	 * Set Presence
	 *
	 * @param string $status
	 * @param string $show
	 * @param string $to
	 */
	public function presence($status = null, $show = 'available', $to = null, $type='available', $priority=0) {
		if($type == 'available') $type = '';
		$to	 = htmlspecialchars($to);
		$status = htmlspecialchars($status);
		if($show == 'unavailable') $type = 'unavailable';

		$out = "<presence";
		if($to) $out .= " to=\"$to\"";
		if($type) $out .= " type='$type'";
		if($show == 'available' and !$status) {
			$out .= "/>";
		} else {
			$out .= ">";
			if($show != 'available') $out .= "<show>$show</show>";
			if($status) $out .= "<status>$status</status>";
			if($priority) $out .= "<priority>$priority</priority>";
			$out .= "</presence>";
		}

		$this->send($out);
	}
	/**
	 * Send Auth request
	 *
	 * @param string $jid
	 */
	public function subscribe($jid) {
		$this->send("<presence type='subscribe' to='{$jid}' from='{$this->fulljid}' />");
		#$this->send("<presence type='subscribed' to='{$jid}' from='{$this->fulljid}' />");
	}

	/**
	 * Message handler
	 *
	 * @param string $xml
	 */
	public function message_handler($xml) {
		if(isset($xml->attrs['type'])) {
			$payload['type'] = $xml->attrs['type'];
		} else {
			$payload['type'] = 'chat';
		}
		$payload['from'] = $xml->attrs['from'];
		$payload['body'] = $xml->sub('body')->data;
		$payload['xml'] = $xml;
		$this->log->log("Message: {$xml->sub('body')->data}", XMPPHP_Log::LEVEL_DEBUG);
		$this->event('message', $payload);
	}

	/**
	 * Presence handler
	 *
	 * @param string $xml
	 */
	public function presence_handler($xml) {
		$payload['type'] = (isset($xml->attrs['type'])) ? $xml->attrs['type'] : 'available';
		$payload['show'] = (isset($xml->sub('show')->data)) ? $xml->sub('show')->data : $payload['type'];
		$payload['from'] = $xml->attrs['from'];
		$payload['status'] = (isset($xml->sub('status')->data)) ? $xml->sub('status')->data : '';
		$payload['priority'] = (isset($xml->sub('priority')->data)) ? intval($xml->sub('priority')->data) : 0;
		$payload['xml'] = $xml;
		if($this->track_presence) {
			$this->roster->setPresence($payload['from'], $payload['priority'], $payload['show'], $payload['status']);
		}
		$this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}",  XMPPHP_Log::LEVEL_DEBUG);
		if(array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribe') {
			if($this->auto_subscribe) {
				$this->send("<presence type='subscribed' to='{$xml->attrs['from']}' from='{$this->fulljid}' />");
				$this->send("<presence type='subscribe' to='{$xml->attrs['from']}' from='{$this->fulljid}' />");
			}
			$this->event('subscription_requested', $payload);
		} elseif(array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribed') {
			$this->event('subscription_accepted', $payload);
		} else {
			$this->event('presence', $payload);
		}
	}

	/**
	 * Features handler
	 *
	 * @param string $xml
	 */
	protected function features_handler($xml) {
		if($xml->hasSub('starttls') and $this->use_encryption) {
			$this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required /></starttls>");
		} elseif($xml->hasSub('bind') and $this->authed) {
			$id = $this->getId();
			$this->addIdHandler($id, 'resource_bind_handler');
			$this->send("<iq xmlns=\"jabber:client\" type=\"set\" id=\"$id\"><bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"><resource>{$this->resource}</resource></bind></iq>");
		} else {
			$this->log->log("Attempting Auth...");
			if ($this->password) {
			$this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . "</auth>");
			} else {
                        $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>");
			}
		}
	}

	/**
	 * SASL success handler
	 *
	 * @param string $xml
	 */
	protected function sasl_success_handler($xml) {
		$this->log->log("Auth success!");
		$this->authed = true;
		$this->reset();
	}

	/**
	 * SASL feature handler
	 *
	 * @param string $xml
	 */
	protected function sasl_failure_handler($xml) {
		$this->log->log("Auth failed!",  XMPPHP_Log::LEVEL_ERROR);
		$this->disconnect();

		throw new XMPPHP_Exception('Auth failed!');
	}

	/**
	 * Resource bind handler
	 *
	 * @param string $xml
	 */
	protected function resource_bind_handler($xml) {
		if($xml->attrs['type'] == 'result') {
			$this->log->log("Bound to " . $xml->sub('bind')->sub('jid')->data);
			$this->fulljid = $xml->sub('bind')->sub('jid')->data;
			$jidarray = explode('/',$this->fulljid);
			$this->jid = $jidarray[0];
		}
		$id = $this->getId();
		$this->addIdHandler($id, 'session_start_handler');
		$this->send("<iq xmlns='jabber:client' type='set' id='$id'><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></iq>");
	}

	/**
	* Retrieves the roster
	*
	*/
	public function getRoster() {
		$id = $this->getID();
		$this->send("<iq xmlns='jabber:client' type='get' id='$id'><query xmlns='jabber:iq:roster' /></iq>");
	}

	/**
	* Roster iq handler
	* Gets all packets matching XPath "iq/{jabber:iq:roster}query'
	*
	* @param string $xml
	*/
	protected function roster_iq_handler($xml) {
		$status = "result";
		$xmlroster = $xml->sub('query');
		foreach($xmlroster->subs as $item) {
			$groups = array();
			if ($item->name == 'item') {
				$jid = $item->attrs['jid']; //REQUIRED
				$name = $item->attrs['name']; //MAY
				$subscription = $item->attrs['subscription'];
				foreach($item->subs as $subitem) {
					if ($subitem->name == 'group') {
						$groups[] = $subitem->data;
					}
				}
				$contacts[] = array($jid, $subscription, $name, $groups); //Store for action if no errors happen
			} else {
				$status = "error";
			}
		}
		if ($status == "result") { //No errors, add contacts
			foreach($contacts as $contact) {
				$this->roster->addContact($contact[0], $contact[1], $contact[2], $contact[3]);
			}
		}
		if ($xml->attrs['type'] == 'set') {
			$this->send("<iq type=\"reply\" id=\"{$xml->attrs['id']}\" to=\"{$xml->attrs['from']}\" />");
		}
	}

	/**
	 * Session start handler
	 *
	 * @param string $xml
	 */
	protected function session_start_handler($xml) {
		$this->log->log("Session started");
		$this->session_started = true;
		$this->event('session_start');
	}

	/**
	 * TLS proceed handler
	 *
	 * @param string $xml
	 */
	protected function tls_proceed_handler($xml) {
		$this->log->log("Starting TLS encryption");
		stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
		$this->reset();
	}

	/**
	* Retrieves the vcard
	*
	*/
	public function getVCard($jid = Null) {
		$id = $this->getID();
		$this->addIdHandler($id, 'vcard_get_handler');
		if($jid) {
			$this->send("<iq type='get' id='$id' to='$jid'><vCard xmlns='vcard-temp' /></iq>");
		} else {
			$this->send("<iq type='get' id='$id'><vCard xmlns='vcard-temp' /></iq>");
		}
	}

	/**
	* VCard retrieval handler
	*
	* @param XML Object $xml
	*/
	protected function vcard_get_handler($xml) {
		$vcard_array = array();
		$vcard = $xml->sub('vcard');
		// go through all of the sub elements and add them to the vcard array
		foreach ($vcard->subs as $sub) {
			if ($sub->subs) {
				$vcard_array[$sub->name] = array();
				foreach ($sub->subs as $sub_child) {
					$vcard_array[$sub->name][$sub_child->name] = $sub_child->data;
				}
			} else {
				$vcard_array[$sub->name] = $sub->data;
			}
		}
		$vcard_array['from'] = $xml->attrs['from'];
		$this->event('vcard', $vcard_array);
	}
}


code:
<?php
/**
 * XMPPHP: The PHP XMPP Library
 * Copyright (C) 2008  Nathanael C. Fritz
 * This file is part of SleekXMPP.
 *
 * XMPPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XMPPHP 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XMPPHP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category   xmpphp
 * @package	XMPPHP
 * @author	 Nathanael C. Fritz <JID: fritzy@netflint.net>
 * @author	 Stephan Wentz <JID: stephan@jabber.wentz.it>
 * @author	 Michael Garvin <JID: gar@netflint.net>
 * @copyright  2008 Nathanael C. Fritz
 */

/** XMPPHP_XMPP
 *
 * This file is unnecessary unless you need to connect to older, non-XMPP-compliant servers like Dreamhost's.
 * In this case, use instead of XMPPHP_XMPP, otherwise feel free to delete it.
 * The old Jabber protocol wasn't standardized, so use at your own risk.
 *
 */
require_once "XMPP.php";

	class XMPPHP_XMPPOld extends XMPPHP_XMPP {
		/**
		 *
		 * @var string
		 */
		protected $session_id;

		public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null) {
			parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
			if(!$server) $server = $host;
			$this->stream_start = '<stream:stream to="' . $server . '" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">';
			$this->fulljid = "{$user}@{$server}/{$resource}";
		}

		/**
		 * Override XMLStream's startXML
		 *
		 * @param parser $parser
		 * @param string $name
		 * @param array $attr
		 */
		public function startXML($parser, $name, $attr) {
			if($this->xml_depth == 0) {
				$this->session_id = $attr['ID'];
				$this->authenticate();
			}
			parent::startXML($parser, $name, $attr);
		}

		/**
		 * Send Authenticate Info Request
		 *
		 */
		public function authenticate() {
			$id = $this->getId();
			$this->addidhandler($id, 'authfieldshandler');
			$this->send("<iq type='get' id='$id'><query xmlns='jabber:iq:auth'><username>{$this->user}</username></query></iq>");
		}

		/**
		 * Retrieve auth fields and send auth attempt
		 *
		 * @param XMLObj $xml
		 */
		public function authFieldsHandler($xml) {
			$id = $this->getId();
			$this->addidhandler($id, 'oldAuthResultHandler');
			if($xml->sub('query')->hasSub('digest')) {
				$hash = sha1($this->session_id . $this->password);
				print "{$this->session_id} {$this->password}\n";
				$out = "<iq type='set' id='$id'><query xmlns='jabber:iq:auth'><username>{$this->user}</username><digest>{$hash}</digest><resource>{$this->resource}</resource></query></iq>";
			} else {
				$out = "<iq type='set' id='$id'><query xmlns='jabber:iq:auth'><username>{$this->user}</username><password>{$this->password}</password><resource>{$this->resource}</resource></query></iq>";
			}
			$this->send($out);

		}

		/**
		 * Determine authenticated or failure
		 *
		 * @param XMLObj $xml
		 */
		public function oldAuthResultHandler($xml) {
			if($xml->attrs['type'] != 'result') {
				$this->log->log("Auth failed!",  XMPPHP_Log::LEVEL_ERROR);
				$this->disconnect();
				throw new XMPPHP_Exception('Auth failed!');
			} else {
				$this->log->log("Session started");
				$this->event('session_start');
			}
		}
	}

?>


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Queue-mediated proxy class for outgoing XMPP messages.
 *
 * 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  Network
 * @package   StatusNet
 * @author    Brion Vibber <brion@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

class QueuedXMPP extends XMPPHP_XMPP
{
    /**
     * Reference to the XmppPlugin object we're hooked up to.
     */
    public $plugin;

	/**
	 * Constructor
	 *
     * @param XmppPlugin $plugin
	 * @param string  $host
	 * @param integer $port
	 * @param string  $user
	 * @param string  $password
	 * @param string  $resource
	 * @param string  $server
	 * @param boolean $printlog
	 * @param string  $loglevel
	 */
	public function __construct($plugin, $host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null)
	{
        $this->plugin = $plugin;

        parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);

        // We use $host to connect, but $server to build JIDs if specified.
        // This seems to fix an upstream bug where $host was used to build
        // $this->basejid, never seen since it isn't actually used in the base
        // classes.
        if (!$server) {
            $server = $this->host;
        }
        $this->basejid = $this->user . '@' . $server;

        // Normally the fulljid is filled out by the server at resource binding
        // time, but we need to do it since we're not talking to a real server.
        $this->fulljid = "{$this->basejid}/{$this->resource}";
    }

    /**
     * Send a formatted message to the outgoing queue for later forwarding
     * to a real XMPP connection.
     *
     * @param string $msg
     */
    public function send($msg, $timeout=NULL)
    {
        $this->plugin->enqueueOutgoingRaw($msg);
    }

    //@{
    /**
     * Stream i/o functions disabled; only do output
     */
    public function connect($timeout = 30, $persistent = false, $sendinit = true)
    {
        // No i18n needed. Test message.
        throw new Exception('Cannot connect to server from fake XMPP.');
    }

    public function disconnect()
    {
        // No i18n needed. Test message.
        throw new Exception('Cannot connect to server from fake XMPP.');
    }

    public function process()
    {
        // No i18n needed. Test message.
        throw new Exception('Cannot read stream from fake XMPP.');
    }

    public function processUntil($event, $timeout=-1)
    {
        // No i18n needed. Test message.
        throw new Exception('Cannot read stream from fake XMPP.');
    }

    public function read()
    {
        // No i18n needed. Test message.
        throw new Exception('Cannot read stream from fake XMPP.');
    }

    public function readyToProcess()
    {
        // No i18n needed. Test message.
        throw new Exception('Cannot read stream from fake XMPP.');
    }
    //@}
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2009, StatusNet, Inc.
 *
 * Send and receive notices using the Jabber network
 *
 * 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  Jabber
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

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

class SharingXMPP extends XMPPHP_XMPP
{
    function getSocket()
    {
        return $this->socket;
    }
}