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