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