See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311]
1 <?php 2 /** 3 * XMPPHP: The PHP XMPP Library 4 * Copyright (C) 2008 Nathanael C. Fritz 5 * This file is part of SleekXMPP. 6 * 7 * XMPPHP is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * XMPPHP is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with XMPPHP; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 * 21 * @category xmpphp 22 * @package XMPPHP 23 * @author Nathanael C. Fritz <JID: fritzy@netflint.net> 24 * @author Stephan Wentz <JID: stephan@jabber.wentz.it> 25 * @author Michael Garvin <JID: gar@netflint.net> 26 * @copyright 2008 Nathanael C. Fritz 27 */ 28 29 /** XMPPHP_XMLStream */ 30 require_once dirname(__FILE__) . "/XMLStream.php"; 31 require_once dirname(__FILE__) . "/Roster.php"; 32 33 /** 34 * XMPPHP Main Class 35 * 36 * @category xmpphp 37 * @package XMPPHP 38 * @author Nathanael C. Fritz <JID: fritzy@netflint.net> 39 * @author Stephan Wentz <JID: stephan@jabber.wentz.it> 40 * @author Michael Garvin <JID: gar@netflint.net> 41 * @copyright 2008 Nathanael C. Fritz 42 * @version $Id$ 43 */ 44 class XMPPHP_XMPP extends XMPPHP_XMLStream { 45 /** 46 * @var string 47 */ 48 public $server; 49 50 /** 51 * @var string 52 */ 53 public $user; 54 55 /** 56 * @var string 57 */ 58 protected $password; 59 60 /** 61 * @var string 62 */ 63 protected $resource; 64 65 /** 66 * @var string 67 */ 68 protected $fulljid; 69 70 /** 71 * @var string 72 */ 73 protected $basejid; 74 75 /** 76 * @var boolean 77 */ 78 protected $authed = false; 79 protected $session_started = false; 80 81 /** 82 * @var boolean 83 */ 84 protected $auto_subscribe = false; 85 86 /** 87 * @var boolean 88 */ 89 protected $use_encryption = true; 90 91 /** 92 * @var boolean 93 */ 94 public $track_presence = true; 95 96 /** 97 * @var object 98 */ 99 public $roster; 100 101 /** 102 * Constructor 103 * 104 * @param string $host 105 * @param integer $port 106 * @param string $user 107 * @param string $password 108 * @param string $resource 109 * @param string $server 110 * @param boolean $printlog 111 * @param string $loglevel 112 */ 113 public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null) { 114 parent::__construct($host, $port, $printlog, $loglevel); 115 116 $this->user = $user; 117 $this->password = $password; 118 $this->resource = $resource; 119 if(!$server) $server = $host; 120 $this->basejid = $this->user . '@' . $this->host; 121 122 $this->roster = new Roster(); 123 $this->track_presence = true; 124 125 $this->stream_start = '<stream:stream to="' . $server . '" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0">'; 126 $this->stream_end = '</stream:stream>'; 127 $this->default_ns = 'jabber:client'; 128 129 $this->addXPathHandler('{http://etherx.jabber.org/streams}features', 'features_handler'); 130 $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}success', 'sasl_success_handler'); 131 $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}failure', 'sasl_failure_handler'); 132 $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-tls}proceed', 'tls_proceed_handler'); 133 $this->addXPathHandler('{jabber:client}message', 'message_handler'); 134 $this->addXPathHandler('{jabber:client}presence', 'presence_handler'); 135 $this->addXPathHandler('iq/{jabber:iq:roster}query', 'roster_iq_handler'); 136 } 137 138 /** 139 * Turn encryption on/ff 140 * 141 * @param boolean $useEncryption 142 */ 143 public function useEncryption($useEncryption = true) { 144 $this->use_encryption = $useEncryption; 145 } 146 147 /** 148 * Turn on auto-authorization of subscription requests. 149 * 150 * @param boolean $autoSubscribe 151 */ 152 public function autoSubscribe($autoSubscribe = true) { 153 $this->auto_subscribe = $autoSubscribe; 154 } 155 156 /** 157 * Send XMPP Message 158 * 159 * @param string $to 160 * @param string $body 161 * @param string $type 162 * @param string $subject 163 */ 164 public function message($to, $body, $type = 'chat', $subject = null, $payload = null) { 165 if(is_null($type)) 166 { 167 $type = 'chat'; 168 } 169 170 $to = htmlspecialchars($to); 171 $body = htmlspecialchars($body); 172 $subject = htmlspecialchars($subject); 173 174 $out = "<message from=\"{$this->fulljid}\" to=\"$to\" type='$type'>"; 175 if($subject) $out .= "<subject>$subject</subject>"; 176 $out .= "<body>$body</body>"; 177 if($payload) $out .= $payload; 178 $out .= "</message>"; 179 180 $this->send($out); 181 } 182 183 /** 184 * Set Presence 185 * 186 * @param string $status 187 * @param string $show 188 * @param string $to 189 */ 190 public function presence($status = null, $show = 'available', $to = null, $type='available', $priority=0) { 191 if($type == 'available') $type = ''; 192 $to = htmlspecialchars($to); 193 $status = htmlspecialchars($status); 194 if($show == 'unavailable') $type = 'unavailable'; 195 196 $out = "<presence"; 197 if($to) $out .= " to=\"$to\""; 198 if($type) $out .= " type='$type'"; 199 if($show == 'available' and !$status) { 200 $out .= "/>"; 201 } else { 202 $out .= ">"; 203 if($show != 'available') $out .= "<show>$show</show>"; 204 if($status) $out .= "<status>$status</status>"; 205 if($priority) $out .= "<priority>$priority</priority>"; 206 $out .= "</presence>"; 207 } 208 209 $this->send($out); 210 } 211 /** 212 * Send Auth request 213 * 214 * @param string $jid 215 */ 216 public function subscribe($jid) { 217 $this->send("<presence type='subscribe' to='{$jid}' from='{$this->fulljid}' />"); 218 #$this->send("<presence type='subscribed' to='{$jid}' from='{$this->fulljid}' />"); 219 } 220 221 /** 222 * Message handler 223 * 224 * @param string $xml 225 */ 226 public function message_handler($xml) { 227 if(isset($xml->attrs['type'])) { 228 $payload['type'] = $xml->attrs['type']; 229 } else { 230 $payload['type'] = 'chat'; 231 } 232 $payload['from'] = $xml->attrs['from']; 233 $payload['body'] = $xml->sub('body')->data; 234 $payload['xml'] = $xml; 235 $this->log->log("Message: {$xml->sub('body')->data}", XMPPHP_Log::LEVEL_DEBUG); 236 $this->event('message', $payload); 237 } 238 239 /** 240 * Presence handler 241 * 242 * @param string $xml 243 */ 244 public function presence_handler($xml) { 245 $payload['type'] = (isset($xml->attrs['type'])) ? $xml->attrs['type'] : 'available'; 246 $payload['show'] = (isset($xml->sub('show')->data)) ? $xml->sub('show')->data : $payload['type']; 247 $payload['from'] = $xml->attrs['from']; 248 $payload['status'] = (isset($xml->sub('status')->data)) ? $xml->sub('status')->data : ''; 249 $payload['priority'] = (isset($xml->sub('priority')->data)) ? intval($xml->sub('priority')->data) : 0; 250 $payload['xml'] = $xml; 251 if($this->track_presence) { 252 $this->roster->setPresence($payload['from'], $payload['priority'], $payload['show'], $payload['status']); 253 } 254 $this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}", XMPPHP_Log::LEVEL_DEBUG); 255 if(array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribe') { 256 if($this->auto_subscribe) { 257 $this->send("<presence type='subscribed' to='{$xml->attrs['from']}' from='{$this->fulljid}' />"); 258 $this->send("<presence type='subscribe' to='{$xml->attrs['from']}' from='{$this->fulljid}' />"); 259 } 260 $this->event('subscription_requested', $payload); 261 } elseif(array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribed') { 262 $this->event('subscription_accepted', $payload); 263 } else { 264 $this->event('presence', $payload); 265 } 266 } 267 268 /** 269 * Features handler 270 * 271 * @param string $xml 272 */ 273 protected function features_handler($xml) { 274 if($xml->hasSub('starttls') and $this->use_encryption) { 275 $this->send("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required /></starttls>"); 276 } elseif($xml->hasSub('bind') and $this->authed) { 277 $id = $this->getId(); 278 $this->addIdHandler($id, 'resource_bind_handler'); 279 $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>"); 280 } else { 281 $this->log->log("Attempting Auth..."); 282 if ($this->password) { 283 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . "</auth>"); 284 } else { 285 $this->send("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>"); 286 } 287 } 288 } 289 290 /** 291 * SASL success handler 292 * 293 * @param string $xml 294 */ 295 protected function sasl_success_handler($xml) { 296 $this->log->log("Auth success!"); 297 $this->authed = true; 298 $this->reset(); 299 } 300 301 /** 302 * SASL feature handler 303 * 304 * @param string $xml 305 */ 306 protected function sasl_failure_handler($xml) { 307 $this->log->log("Auth failed!", XMPPHP_Log::LEVEL_ERROR); 308 $this->disconnect(); 309 310 throw new XMPPHP_Exception('Auth failed!'); 311 } 312 313 /** 314 * Resource bind handler 315 * 316 * @param string $xml 317 */ 318 protected function resource_bind_handler($xml) { 319 if($xml->attrs['type'] == 'result') { 320 $this->log->log("Bound to " . $xml->sub('bind')->sub('jid')->data); 321 $this->fulljid = $xml->sub('bind')->sub('jid')->data; 322 $jidarray = explode('/',$this->fulljid); 323 $this->jid = $jidarray[0]; 324 } 325 $id = $this->getId(); 326 $this->addIdHandler($id, 'session_start_handler'); 327 $this->send("<iq xmlns='jabber:client' type='set' id='$id'><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></iq>"); 328 } 329 330 /** 331 * Retrieves the roster 332 * 333 */ 334 public function getRoster() { 335 $id = $this->getID(); 336 $this->send("<iq xmlns='jabber:client' type='get' id='$id'><query xmlns='jabber:iq:roster' /></iq>"); 337 } 338 339 /** 340 * Roster iq handler 341 * Gets all packets matching XPath "iq/{jabber:iq:roster}query' 342 * 343 * @param string $xml 344 */ 345 protected function roster_iq_handler($xml) { 346 $status = "result"; 347 $xmlroster = $xml->sub('query'); 348 foreach($xmlroster->subs as $item) { 349 $groups = array(); 350 if ($item->name == 'item') { 351 $jid = $item->attrs['jid']; //REQUIRED 352 $name = $item->attrs['name']; //MAY 353 $subscription = $item->attrs['subscription']; 354 foreach($item->subs as $subitem) { 355 if ($subitem->name == 'group') { 356 $groups[] = $subitem->data; 357 } 358 } 359 $contacts[] = array($jid, $subscription, $name, $groups); //Store for action if no errors happen 360 } else { 361 $status = "error"; 362 } 363 } 364 if ($status == "result") { //No errors, add contacts 365 foreach($contacts as $contact) { 366 $this->roster->addContact($contact[0], $contact[1], $contact[2], $contact[3]); 367 } 368 } 369 if ($xml->attrs['type'] == 'set') { 370 $this->send("<iq type=\"reply\" id=\"{$xml->attrs['id']}\" to=\"{$xml->attrs['from']}\" />"); 371 } 372 } 373 374 /** 375 * Session start handler 376 * 377 * @param string $xml 378 */ 379 protected function session_start_handler($xml) { 380 $this->log->log("Session started"); 381 $this->session_started = true; 382 $this->event('session_start'); 383 } 384 385 /** 386 * TLS proceed handler 387 * 388 * @param string $xml 389 */ 390 protected function tls_proceed_handler($xml) { 391 $this->log->log("Starting TLS encryption"); 392 stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT); 393 $this->reset(); 394 } 395 396 /** 397 * Retrieves the vcard 398 * 399 */ 400 public function getVCard($jid = Null) { 401 $id = $this->getID(); 402 $this->addIdHandler($id, 'vcard_get_handler'); 403 if($jid) { 404 $this->send("<iq type='get' id='$id' to='$jid'><vCard xmlns='vcard-temp' /></iq>"); 405 } else { 406 $this->send("<iq type='get' id='$id'><vCard xmlns='vcard-temp' /></iq>"); 407 } 408 } 409 410 /** 411 * VCard retrieval handler 412 * 413 * @param XML Object $xml 414 */ 415 protected function vcard_get_handler($xml) { 416 $vcard_array = array(); 417 $vcard = $xml->sub('vcard'); 418 // go through all of the sub elements and add them to the vcard array 419 foreach ($vcard->subs as $sub) { 420 if ($sub->subs) { 421 $vcard_array[$sub->name] = array(); 422 foreach ($sub->subs as $sub_child) { 423 $vcard_array[$sub->name][$sub_child->name] = $sub_child->data; 424 } 425 } else { 426 $vcard_array[$sub->name] = $sub->data; 427 } 428 } 429 $vcard_array['from'] = $xml->attrs['from']; 430 $this->event('vcard', $vcard_array); 431 } 432 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body