diff --git a/XMPPHP/BOSH.php b/XMPPHP/BOSH.php
index dacb3b4..49e977d 100644
--- a/XMPPHP/BOSH.php
+++ b/XMPPHP/BOSH.php
@@ -27,7 +27,7 @@
*/
/** XMPPHP_XMLStream */
-require_once 'XMPPHP/XMPP.php';
+require_once dirname(__FILE__) . '/XMPP.php';
/**
* XMPPHP Main Class
diff --git a/XMPPHP/XMLStream.php b/XMPPHP/XMLStream.php
index a39e12e..d818881 100644
--- a/XMPPHP/XMLStream.php
+++ b/XMPPHP/XMLStream.php
@@ -27,13 +27,13 @@
*/
/** XMPPHP_Exception */
-require_once 'XMPPHP/Exception.php';
+require_once dirname(__FILE__) . '/Exception.php';
/** XMPPHP_XMLObj */
-require_once 'XMPPHP/XMLObj.php';
+require_once dirname(__FILE__) . '/XMLObj.php';
/** XMPPHP_Log */
-require_once 'XMPPHP/Log.php';
+require_once dirname(__FILE__) . '/Log.php';
/**
* XMPPHP XML Stream
@@ -465,8 +465,7 @@ class XMPPHP_XMLStream {
}
$part = fread($this->socket, 4096);
stream_set_blocking($this->socket, 1);
-
- if (!$part) {
+ if ($part === false) {
if($this->reconnect) {
$this->doReconnect();
} else {
diff --git a/XMPPHP/XMPP.php b/XMPPHP/XMPP.php
index 0a442bd..33031cb 100644
--- a/XMPPHP/XMPP.php
+++ b/XMPPHP/XMPP.php
@@ -27,8 +27,8 @@
*/
/** XMPPHP_XMLStream */
-require_once 'XMPPHP/XMLStream.php';
-require_once 'XMPPHP/Roster.php';
+require_once dirname(__FILE__) . '/XMLStream.php';
+require_once dirname(__FILE__) . '/Roster.php';
/**
* XMPPHP Main Class
@@ -98,6 +98,21 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
*/
public $roster;
+ /**
+ * @var array supported auth mechanisms
+ */
+ protected $auth_mechanism_supported = array('PLAIN', 'DIGEST-MD5');
+
+ /**
+ * @var string default auth mechanism
+ */
+ protected $auth_mechanism_default = 'PLAIN';
+
+ /**
+ * @var string prefered auth mechanism
+ */
+ protected $auth_mechanism_preferred = 'DIGEST-MD5';
+
/**
* Constructor
*
@@ -117,6 +132,7 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
$this->password = $password;
$this->resource = $resource;
if(!$server) $server = $host;
+ $this->server = $server;
$this->basejid = $this->user . '@' . $this->host;
$this->roster = new Roster();
@@ -133,6 +149,8 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
$this->addXPathHandler('{jabber:client}message', 'message_handler');
$this->addXPathHandler('{jabber:client}presence', 'presence_handler');
$this->addXPathHandler('iq/{jabber:iq:roster}query', 'roster_iq_handler');
+ // For DIGEST-MD5 auth :
+ $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}challenge', 'sasl_challenge_handler');
}
/**
@@ -218,6 +236,42 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
#$this->send("");
}
+ /**
+ * Add user to Roster
+ *
+ * @param string $jid user jid
+ * @param string $name user nickname
+ * @param string $group group to add
+ */
+ public function RosterAddUser($jid, $name=null, $group=null) {
+ $payload = " \n".
+ ($group?''.htmlspecialchars($group, ENT_QUOTES, 'UTF-8').'':'');
+ $this->SendIq(NULL, 'set', "jabber:iq:roster", $payload);
+ }
+
+ /**
+ * Send ID action
+ *
+ * @param string $to to jid
+ * @param string $type type of ID
+ * @param string $xmlns xmlns name
+ * @param string $payload payload string
+ * @param string $from from jid
+ */
+ private function sendIq($to = NULL, $type = 'get', $xmlns = NULL, $payload = NULL, $from = NULL)
+ {
+ $id = $this->getID();
+ $xml = "
+
+ $payload
+
+ ";
+ return $this->send($xml);
+ }
+
/**
* Message handler
*
@@ -281,7 +335,33 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
} else {
$this->log->log("Attempting Auth...");
if ($this->password) {
- $this->send("" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . "");
+ $mechanism = 'PLAIN'; // default;
+ if ($xml->hasSub('mechanisms') && $xml->sub('mechanisms')->hasSub('mechanism')) {
+ // Get the list of all available auth mechanism that we can use
+ $available = array();
+ foreach ($xml->sub('mechanisms')->subs as $sub) {
+ if ($sub->name == 'mechanism') {
+ if (in_array($sub->data, $this->auth_mechanism_supported)) {
+ $available[$sub->data] = $sub->data;
+ }
+ }
+ }
+ if (isset($available[$this->auth_mechanism_preferred])) {
+ $mechanism = $this->auth_mechanism_preferred;
+ } else {
+ // use the first available
+ $mechanism = reset($available);
+ }
+ $this->log->log("Trying $mechanism (available : " . implode(',', $available) . ')');
+ }
+ switch ($mechanism) {
+ case 'PLAIN':
+ $this->send("" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . "");
+ break;
+ case 'DIGEST-MD5':
+ $this->send("");
+ break;
+ }
} else {
$this->send("");
}
@@ -311,6 +391,56 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
throw new XMPPHP_Exception('Auth failed!');
}
+ /**
+ * Handle challenges for DIGEST-MD5 auth
+ *
+ * @param string $xml
+ */
+ protected function sasl_challenge_handler($xml) {
+ // Decode and parse the challenge string
+ // (may be something like foo="bar",foo2="bar2,bar3,bar4",foo3=bar5 )
+ $challenge = base64_decode($xml->data);
+ $vars = array();
+ $matches = array();
+ preg_match_all('/(\w+)=(?:"([^"]*)|([^,]*))/', $challenge, $matches);
+ $res = array();
+ foreach ($matches[1] as $k => $v) {
+ $vars[$v] = (empty($matches[2][$k])?$matches[3][$k]:$matches[2][$k]);
+ }
+
+ if (isset($vars['nonce'])) {
+ // First step
+ $vars['cnonce'] = uniqid(mt_rand(), false);
+ $vars['nc'] = '00000001';
+ $vars['qop'] = 'auth'; // Force qop to auth
+ if (!isset($vars['digest-uri'])) $vars['digest-uri'] = 'xmpp/' . $this->server;
+ if (!isset($vars['realm'])) $vars['realm'] = '';
+
+ // now, the magic...
+ $a1 = sprintf('%s:%s:%s', $this->user, $vars['realm'], $this->password);
+ if ($vars['algorithm'] == 'md5-sess') {
+ $a1 = pack('H32',md5($a1)) . ':' . $vars['nonce'] . ':' . $vars['cnonce'];
+ }
+ $a2 = "AUTHENTICATE:" . $vars['digest-uri'];
+ $password = md5($a1) . ':' . $vars['nonce'] . ':' . $vars['nc'] . ':' . $vars['cnonce'] . ':' . $vars['qop'] . ':' .md5($a2);
+ $password = md5($password);
+ $response = sprintf('username="%s",realm="%s",nonce="%s",cnonce="%s",nc=%s,qop=%s,digest-uri="%s",response=%s,charset=utf-8',
+ $this->user, $vars['realm'], $vars['nonce'], $vars['cnonce'], $vars['nc'], $vars['qop'], $vars['digest-uri'], $password);
+
+ // Send the response
+ $response = base64_encode($response);
+ $this->send("$response");
+ } else {
+ if (isset($vars['rspauth'])) {
+ // Second step
+ $this->send("");
+ } else {
+ $this->log->log("ERROR receiving challenge : " . $challenge, XMPPHP_Log::LEVEL_ERROR);
+ }
+
+ }
+ }
+
/**
* Resource bind handler
*
diff --git a/XMPPHP/XMPP_Old.php b/XMPPHP/XMPP_Old.php
index 43f56b1..6083dad 100644
--- a/XMPPHP/XMPP_Old.php
+++ b/XMPPHP/XMPP_Old.php
@@ -33,7 +33,7 @@
* The old Jabber protocol wasn't standardized, so use at your own risk.
*
*/
-require_once "XMPP.php";
+require_once dirname(__FILE__) . '/XMPP.php';
class XMPPHP_XMPPOld extends XMPPHP_XMPP {
/**
diff --git a/examples/cli_longrun_example.php b/examples/cli_longrun_example.php
index 59bb29d..e15b924 100644
--- a/examples/cli_longrun_example.php
+++ b/examples/cli_longrun_example.php
@@ -3,7 +3,7 @@
// activate full error reporting
//error_reporting(E_ALL & E_STRICT);
-include 'XMPPHP/XMPP.php';
+include dirname(dirname(__FILE__)).'/XMPPHP/XMPP.php';
#Use XMPPHP_Log::LEVEL_VERBOSE to get more logging for error reports
#If this doesn't work, are you running 64-bit PHP with < 5.2.6?
diff --git a/examples/cli_longrun_example_bosh.php b/examples/cli_longrun_example_bosh.php
index 21b63f1..80da540 100644
--- a/examples/cli_longrun_example_bosh.php
+++ b/examples/cli_longrun_example_bosh.php
@@ -3,7 +3,7 @@
// activate full error reporting
//error_reporting(E_ALL & E_STRICT);
-include 'XMPPHP/BOSH.php';
+include dirname(dirname(__FILE__)).'/XMPPHP/BOSH.php';
#Use XMPPHP_Log::LEVEL_VERBOSE to get more logging for error reports
#If this doesn't work, are you running 64-bit PHP with < 5.2.6?
diff --git a/examples/sendmessage_example.php b/examples/sendmessage_example.php
index e506130..60d5e6f 100644
--- a/examples/sendmessage_example.php
+++ b/examples/sendmessage_example.php
@@ -3,7 +3,7 @@
// activate full error reporting
//error_reporting(E_ALL & E_STRICT);
-include 'XMPPHP/XMPP.php';
+include dirname(dirname(__FILE__)).'/XMPPHP/XMPP.php';
#Use XMPPHP_Log::LEVEL_VERBOSE to get more logging for error reports
#If this doesn't work, are you running 64-bit PHP with < 5.2.6?
diff --git a/examples/webclient_example.php b/examples/webclient_example.php
index eb40e82..fa04f7a 100644
--- a/examples/webclient_example.php
+++ b/examples/webclient_example.php
@@ -4,7 +4,7 @@ header('content-type', 'plain/text');
// activate full error reporting
//error_reporting(E_ALL & E_STRICT);
-include 'XMPPHP/BOSH.php';
+include dirname(dirname(__FILE__)).'/XMPPHP/BOSH.php';
print "
";
#Use XMPPHP_Log::LEVEL_VERBOSE to get more logging for error reports