├── .gitignore ├── LICENSE ├── README ├── XMPPHP ├── BOSH.php ├── Exception.php ├── Log.php ├── Roster.php ├── XMLObj.php ├── XMLStream.php ├── XMPP.php └── XMPP_Old.php ├── cli_longrun_example.php ├── cli_longrun_example_bosh.php ├── composer.json ├── sendmessage_example.php ├── tests ├── AllTests.php └── XMPPHP │ ├── LogTest.php │ ├── XMLObjTest.php │ └── XMPPTest.php └── webclient_example.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.phar 3 | /composer.lock 4 | /nbproject/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | XMPPHP: The PHP XMPP Library 2 | Copyright (C) 2008 Nathanael C. Fritz 3 | 4 | GNU GENERAL PUBLIC LICENSE 5 | Version 2, June 1991 6 | 7 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 8 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 9 | Everyone is permitted to copy and distribute verbatim copies 10 | of this license document, but changing it is not allowed. 11 | 12 | Preamble 13 | 14 | The licenses for most software are designed to take away your 15 | freedom to share and change it. By contrast, the GNU General Public 16 | License is intended to guarantee your freedom to share and change free 17 | software--to make sure the software is free for all its users. This 18 | General Public License applies to most of the Free Software 19 | Foundation's software and to any other program whose authors commit to 20 | using it. (Some other Free Software Foundation software is covered by 21 | the GNU Library General Public License instead.) You can apply it to 22 | your programs, too. 23 | 24 | When we speak of free software, we are referring to freedom, not 25 | price. Our General Public Licenses are designed to make sure that you 26 | have the freedom to distribute copies of free software (and charge for 27 | this service if you wish), that you receive source code or can get it 28 | if you want it, that you can change the software or use pieces of it 29 | in new free programs; and that you know you can do these things. 30 | 31 | To protect your rights, we need to make restrictions that forbid 32 | anyone to deny you these rights or to ask you to surrender the rights. 33 | These restrictions translate to certain responsibilities for you if you 34 | distribute copies of the software, or if you modify it. 35 | 36 | For example, if you distribute copies of such a program, whether 37 | gratis or for a fee, you must give the recipients all the rights that 38 | you have. You must make sure that they, too, receive or can get the 39 | source code. And you must show them these terms so they know their 40 | rights. 41 | 42 | We protect your rights with two steps: (1) copyright the software, and 43 | (2) offer you this license which gives you legal permission to copy, 44 | distribute and/or modify the software. 45 | 46 | Also, for each author's protection and ours, we want to make certain 47 | that everyone understands that there is no warranty for this free 48 | software. If the software is modified by someone else and passed on, we 49 | want its recipients to know that what they have is not the original, so 50 | that any problems introduced by others will not reflect on the original 51 | authors' reputations. 52 | 53 | Finally, any free program is threatened constantly by software 54 | patents. We wish to avoid the danger that redistributors of a free 55 | program will individually obtain patent licenses, in effect making the 56 | program proprietary. To prevent this, we have made it clear that any 57 | patent must be licensed for everyone's free use or not licensed at all. 58 | 59 | The precise terms and conditions for copying, distribution and 60 | modification follow. 61 | 62 | GNU GENERAL PUBLIC LICENSE 63 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 64 | 65 | 0. This License applies to any program or other work which contains 66 | a notice placed by the copyright holder saying it may be distributed 67 | under the terms of this General Public License. The "Program", below, 68 | refers to any such program or work, and a "work based on the Program" 69 | means either the Program or any derivative work under copyright law: 70 | that is to say, a work containing the Program or a portion of it, 71 | either verbatim or with modifications and/or translated into another 72 | language. (Hereinafter, translation is included without limitation in 73 | the term "modification".) Each licensee is addressed as "you". 74 | 75 | Activities other than copying, distribution and modification are not 76 | covered by this License; they are outside its scope. The act of 77 | running the Program is not restricted, and the output from the Program 78 | is covered only if its contents constitute a work based on the 79 | Program (independent of having been made by running the Program). 80 | Whether that is true depends on what the Program does. 81 | 82 | 1. You may copy and distribute verbatim copies of the Program's 83 | source code as you receive it, in any medium, provided that you 84 | conspicuously and appropriately publish on each copy an appropriate 85 | copyright notice and disclaimer of warranty; keep intact all the 86 | notices that refer to this License and to the absence of any warranty; 87 | and give any other recipients of the Program a copy of this License 88 | along with the Program. 89 | 90 | You may charge a fee for the physical act of transferring a copy, and 91 | you may at your option offer warranty protection in exchange for a fee. 92 | 93 | 2. You may modify your copy or copies of the Program or any portion 94 | of it, thus forming a work based on the Program, and copy and 95 | distribute such modifications or work under the terms of Section 1 96 | above, provided that you also meet all of these conditions: 97 | 98 | a) You must cause the modified files to carry prominent notices 99 | stating that you changed the files and the date of any change. 100 | 101 | b) You must cause any work that you distribute or publish, that in 102 | whole or in part contains or is derived from the Program or any 103 | part thereof, to be licensed as a whole at no charge to all third 104 | parties under the terms of this License. 105 | 106 | c) If the modified program normally reads commands interactively 107 | when run, you must cause it, when started running for such 108 | interactive use in the most ordinary way, to print or display an 109 | announcement including an appropriate copyright notice and a 110 | notice that there is no warranty (or else, saying that you provide 111 | a warranty) and that users may redistribute the program under 112 | these conditions, and telling the user how to view a copy of this 113 | License. (Exception: if the Program itself is interactive but 114 | does not normally print such an announcement, your work based on 115 | the Program is not required to print an announcement.) 116 | 117 | These requirements apply to the modified work as a whole. If 118 | identifiable sections of that work are not derived from the Program, 119 | and can be reasonably considered independent and separate works in 120 | themselves, then this License, and its terms, do not apply to those 121 | sections when you distribute them as separate works. But when you 122 | distribute the same sections as part of a whole which is a work based 123 | on the Program, the distribution of the whole must be on the terms of 124 | this License, whose permissions for other licensees extend to the 125 | entire whole, and thus to each and every part regardless of who wrote it. 126 | 127 | Thus, it is not the intent of this section to claim rights or contest 128 | your rights to work written entirely by you; rather, the intent is to 129 | exercise the right to control the distribution of derivative or 130 | collective works based on the Program. 131 | 132 | In addition, mere aggregation of another work not based on the Program 133 | with the Program (or with a work based on the Program) on a volume of 134 | a storage or distribution medium does not bring the other work under 135 | the scope of this License. 136 | 137 | 3. You may copy and distribute the Program (or a work based on it, 138 | under Section 2) in object code or executable form under the terms of 139 | Sections 1 and 2 above provided that you also do one of the following: 140 | 141 | a) Accompany it with the complete corresponding machine-readable 142 | source code, which must be distributed under the terms of Sections 143 | 1 and 2 above on a medium customarily used for software interchange; or, 144 | 145 | b) Accompany it with a written offer, valid for at least three 146 | years, to give any third party, for a charge no more than your 147 | cost of physically performing source distribution, a complete 148 | machine-readable copy of the corresponding source code, to be 149 | distributed under the terms of Sections 1 and 2 above on a medium 150 | customarily used for software interchange; or, 151 | 152 | c) Accompany it with the information you received as to the offer 153 | to distribute corresponding source code. (This alternative is 154 | allowed only for noncommercial distribution and only if you 155 | received the program in object code or executable form with such 156 | an offer, in accord with Subsection b above.) 157 | 158 | The source code for a work means the preferred form of the work for 159 | making modifications to it. For an executable work, complete source 160 | code means all the source code for all modules it contains, plus any 161 | associated interface definition files, plus the scripts used to 162 | control compilation and installation of the executable. However, as a 163 | special exception, the source code distributed need not include 164 | anything that is normally distributed (in either source or binary 165 | form) with the major components (compiler, kernel, and so on) of the 166 | operating system on which the executable runs, unless that component 167 | itself accompanies the executable. 168 | 169 | If distribution of executable or object code is made by offering 170 | access to copy from a designated place, then offering equivalent 171 | access to copy the source code from the same place counts as 172 | distribution of the source code, even though third parties are not 173 | compelled to copy the source along with the object code. 174 | 175 | 4. You may not copy, modify, sublicense, or distribute the Program 176 | except as expressly provided under this License. Any attempt 177 | otherwise to copy, modify, sublicense or distribute the Program is 178 | void, and will automatically terminate your rights under this License. 179 | However, parties who have received copies, or rights, from you under 180 | this License will not have their licenses terminated so long as such 181 | parties remain in full compliance. 182 | 183 | 5. You are not required to accept this License, since you have not 184 | signed it. However, nothing else grants you permission to modify or 185 | distribute the Program or its derivative works. These actions are 186 | prohibited by law if you do not accept this License. Therefore, by 187 | modifying or distributing the Program (or any work based on the 188 | Program), you indicate your acceptance of this License to do so, and 189 | all its terms and conditions for copying, distributing or modifying 190 | the Program or works based on it. 191 | 192 | 6. Each time you redistribute the Program (or any work based on the 193 | Program), the recipient automatically receives a license from the 194 | original licensor to copy, distribute or modify the Program subject to 195 | these terms and conditions. You may not impose any further 196 | restrictions on the recipients' exercise of the rights granted herein. 197 | You are not responsible for enforcing compliance by third parties to 198 | this License. 199 | 200 | 7. If, as a consequence of a court judgment or allegation of patent 201 | infringement or for any other reason (not limited to patent issues), 202 | conditions are imposed on you (whether by court order, agreement or 203 | otherwise) that contradict the conditions of this License, they do not 204 | excuse you from the conditions of this License. If you cannot 205 | distribute so as to satisfy simultaneously your obligations under this 206 | License and any other pertinent obligations, then as a consequence you 207 | may not distribute the Program at all. For example, if a patent 208 | license would not permit royalty-free redistribution of the Program by 209 | all those who receive copies directly or indirectly through you, then 210 | the only way you could satisfy both it and this License would be to 211 | refrain entirely from distribution of the Program. 212 | 213 | If any portion of this section is held invalid or unenforceable under 214 | any particular circumstance, the balance of the section is intended to 215 | apply and the section as a whole is intended to apply in other 216 | circumstances. 217 | 218 | It is not the purpose of this section to induce you to infringe any 219 | patents or other property right claims or to contest validity of any 220 | such claims; this section has the sole purpose of protecting the 221 | integrity of the free software distribution system, which is 222 | implemented by public license practices. Many people have made 223 | generous contributions to the wide range of software distributed 224 | through that system in reliance on consistent application of that 225 | system; it is up to the author/donor to decide if he or she is willing 226 | to distribute software through any other system and a licensee cannot 227 | impose that choice. 228 | 229 | This section is intended to make thoroughly clear what is believed to 230 | be a consequence of the rest of this License. 231 | 232 | 8. If the distribution and/or use of the Program is restricted in 233 | certain countries either by patents or by copyrighted interfaces, the 234 | original copyright holder who places the Program under this License 235 | may add an explicit geographical distribution limitation excluding 236 | those countries, so that distribution is permitted only in or among 237 | countries not thus excluded. In such case, this License incorporates 238 | the limitation as if written in the body of this License. 239 | 240 | 9. The Free Software Foundation may publish revised and/or new versions 241 | of the General Public License from time to time. Such new versions will 242 | be similar in spirit to the present version, but may differ in detail to 243 | address new problems or concerns. 244 | 245 | Each version is given a distinguishing version number. If the Program 246 | specifies a version number of this License which applies to it and "any 247 | later version", you have the option of following the terms and conditions 248 | either of that version or of any later version published by the Free 249 | Software Foundation. If the Program does not specify a version number of 250 | this License, you may choose any version ever published by the Free Software 251 | Foundation. 252 | 253 | 10. If you wish to incorporate parts of the Program into other free 254 | programs whose distribution conditions are different, write to the author 255 | to ask for permission. For software which is copyrighted by the Free 256 | Software Foundation, write to the Free Software Foundation; we sometimes 257 | make exceptions for this. Our decision will be guided by the two goals 258 | of preserving the free status of all derivatives of our free software and 259 | of promoting the sharing and reuse of software generally. 260 | 261 | NO WARRANTY 262 | 263 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 264 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 265 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 266 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 267 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 268 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 269 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 270 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 271 | REPAIR OR CORRECTION. 272 | 273 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 274 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 275 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 276 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 277 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 278 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 279 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 280 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 281 | POSSIBILITY OF SUCH DAMAGES. 282 | 283 | END OF TERMS AND CONDITIONS 284 | 285 | How to Apply These Terms to Your New Programs 286 | 287 | If you develop a new program, and you want it to be of the greatest 288 | possible use to the public, the best way to achieve this is to make it 289 | free software which everyone can redistribute and change under these terms. 290 | 291 | To do so, attach the following notices to the program. It is safest 292 | to attach them to the start of each source file to most effectively 293 | convey the exclusion of warranty; and each file should have at least 294 | the "copyright" line and a pointer to where the full notice is found. 295 | 296 | 297 | Copyright (C) 298 | 299 | This program is free software; you can redistribute it and/or modify 300 | it under the terms of the GNU General Public License as published by 301 | the Free Software Foundation; either version 2 of the License, or 302 | (at your option) any later version. 303 | 304 | This program is distributed in the hope that it will be useful, 305 | but WITHOUT ANY WARRANTY; without even the implied warranty of 306 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 307 | GNU General Public License for more details. 308 | 309 | You should have received a copy of the GNU General Public License 310 | along with this program; if not, write to the Free Software 311 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 312 | 313 | 314 | Also add information on how to contact you by electronic and paper mail. 315 | 316 | If the program is interactive, make it output a short notice like this 317 | when it starts in an interactive mode: 318 | 319 | Gnomovision version 69, Copyright (C) year name of author 320 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 321 | This is free software, and you are welcome to redistribute it 322 | under certain conditions; type `show c' for details. 323 | 324 | The hypothetical commands `show w' and `show c' should show the appropriate 325 | parts of the General Public License. Of course, the commands you use may 326 | be called something other than `show w' and `show c'; they could even be 327 | mouse-clicks or menu items--whatever suits your program. 328 | 329 | You should also get your employer (if you work as a programmer) or your 330 | school, if any, to sign a "copyright disclaimer" for the program, if 331 | necessary. Here is a sample; alter the names: 332 | 333 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 334 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 335 | 336 | , 1 April 1989 337 | Ty Coon, President of Vice 338 | 339 | This General Public License does not permit incorporating your program into 340 | proprietary programs. If your program is a subroutine library, you may 341 | consider it more useful to permit linking proprietary applications with the 342 | library. If this is what you want to do, use the GNU Library General 343 | Public License instead of this License. 344 | 345 | Exceptions: 346 | 347 | As a special exception to this license, the "Enhanced Jabber Integration" 348 | project, which is a plugin for vBulletin, may use and distribute XMPPHP 349 | without extending the GPL license to the parent package. 350 | 351 | If you would like an exception for your free software, please contact the 352 | copyright holder. 353 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | About 2 | ================================================================================ 3 | XMPPHP is an elegant PHP library for XMPP (Jabber, Google Talk, Facebook etc). 4 | 5 | Homepage: http://code.google.com/p/xmpphp 6 | Author: Nathan Fritz, jabber id: fritzy [at] netflint.net 7 | Co-Author: Stephan Wentz, jabber id: stephan [at] jabber.wentz.it 8 | Heshan Wanigasooriya, heshan [at] heidisoft.com 9 | 10 | If you have any questions (no matter how dumb), please send me an IM. I enjoy 11 | helping people with my code. 12 | 13 | 14 | Requirements 15 | ================================================================================ 16 | * PHP 5.x 17 | * SSL Support Compiled 18 | * CURL Support 19 | 20 | History 21 | ================================================================================ 22 | Carlo Zottmann handed me maintenance of Class.Jabber.PHP years and years ago 23 | (2003?). While I did fix some bugs, I never did much with it. I promised many 24 | people that it would return as a PHP5 rewrite. That day has finally come. 25 | 26 | This code is based on my experience with Class.Jabber.PHP, but more closely 27 | related to my Python library, SleekXMPP (http://code.google.com/p/sleekxmpp). 28 | 29 | Documentation 30 | ================================================================================ 31 | For now, look at the examples. In the near future, I'll have better 32 | documentation on the website. 33 | 34 | TODO 35 | ================================================================================ 36 | * Documentation 37 | * MUC Support 38 | 39 | License Exception 40 | =============================================================================== 41 | Please contact Nathan Fritz for library exceptions if you would like to 42 | distribute XMPPHP with a non-GPL compatible license. 43 | 44 | Also, if you would like to distribute XMPPHP as part of a commercial package, 45 | I sell commercial licenses. 46 | -------------------------------------------------------------------------------- /XMPPHP/BOSH.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 28 | */ 29 | /** XMPPHP_XMLStream */ 30 | require_once dirname(__FILE__) . "/XMPP.php"; 31 | 32 | /** 33 | * XMPPHP Main Class 34 | * 35 | * @category xmpphp 36 | * @package XMPPHP 37 | * @author Nathanael C. Fritz 38 | * @author Stephan Wentz 39 | * @author Michael Garvin 40 | * @copyright 2008 Nathanael C. Fritz 41 | * @version $Id$ 42 | */ 43 | class XMPPHP_BOSH extends XMPPHP_XMPP { 44 | 45 | protected $rid; 46 | protected $sid; 47 | protected $http_server; 48 | protected $http_buffer = Array(); 49 | protected $session = false; 50 | 51 | public function connect($server, $wait='1', $session=false) { 52 | $this->http_server = $server; 53 | $this->use_encryption = false; 54 | $this->session = $session; 55 | 56 | $this->rid = 3001; 57 | $this->sid = null; 58 | if ($session) { 59 | $this->loadSession(); 60 | } 61 | if (!$this->sid) { 62 | $body = $this->__buildBody(); 63 | $body->addAttribute('hold', '1'); 64 | $body->addAttribute('to', $this->host); 65 | $body->addAttribute('route', "xmpp:{$this->host}:{$this->port}"); 66 | $body->addAttribute('secure', 'true'); 67 | $body->addAttribute('xmpp:version', '1.6', 'urn:xmpp:xbosh'); 68 | $body->addAttribute('wait', strval($wait)); 69 | $body->addAttribute('ack', '1'); 70 | $body->addAttribute('xmlns:xmpp', 'urn:xmpp:xbosh'); 71 | $buff = ""; 72 | xml_parse($this->parser, $buff, false); 73 | $response = $this->__sendBody($body); 74 | $rxml = new SimpleXMLElement($response); 75 | $this->sid = $rxml['sid']; 76 | } else { 77 | $buff = ""; 78 | xml_parse($this->parser, $buff, false); 79 | } 80 | } 81 | 82 | public function __sendBody($body=null, $recv=true) { 83 | if (!$body) { 84 | $body = $this->__buildBody(); 85 | } 86 | $ch = curl_init($this->http_server); 87 | curl_setopt($ch, CURLOPT_HEADER, 0); 88 | curl_setopt($ch, CURLOPT_POST, 1); 89 | curl_setopt($ch, CURLOPT_POSTFIELDS, $body->asXML()); 90 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 91 | $header = array('Accept-Encoding: gzip, deflate', 'Content-Type: text/xml; charset=utf-8'); 92 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 93 | curl_setopt($ch, CURLOPT_VERBOSE, 0); 94 | $output = ''; 95 | if ($recv) { 96 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 97 | $output = curl_exec($ch); 98 | $this->http_buffer[] = $output; 99 | } 100 | curl_close($ch); 101 | return $output; 102 | } 103 | 104 | public function __buildBody($sub=null) { 105 | $xml = new SimpleXMLElement(""); 106 | $xml->addAttribute('content', 'text/xml; charset=utf-8'); 107 | $xml->addAttribute('rid', $this->rid); 108 | $this->rid += 1; 109 | if ($this->sid) 110 | $xml->addAttribute('sid', $this->sid); 111 | #if($this->sid) $xml->addAttribute('xmlns', 'http://jabber.org/protocol/httpbind'); 112 | $xml->addAttribute('xml:lang', 'en'); 113 | if ($sub) { // ok, so simplexml is lame 114 | $p = dom_import_simplexml($xml); 115 | $c = dom_import_simplexml($sub); 116 | $cn = $p->ownerDocument->importNode($c, true); 117 | $p->appendChild($cn); 118 | $xml = simplexml_import_dom($p); 119 | } 120 | return $xml; 121 | } 122 | 123 | public function __process() { 124 | if ($this->http_buffer) { 125 | $this->__parseBuffer(); 126 | } else { 127 | $this->__sendBody(); 128 | $this->__parseBuffer(); 129 | } 130 | } 131 | 132 | public function __parseBuffer() { 133 | while ($this->http_buffer) { 134 | $idx = key($this->http_buffer); 135 | $buffer = $this->http_buffer[$idx]; 136 | unset($this->http_buffer[$idx]); 137 | if ($buffer) { 138 | $xml = new SimpleXMLElement($buffer); 139 | $children = $xml->xpath('child::node()'); 140 | foreach ($children as $child) { 141 | $buff = $child->asXML(); 142 | $this->log->log("RECV: $buff", XMPPHP_Log::LEVEL_VERBOSE); 143 | xml_parse($this->parser, $buff, false); 144 | } 145 | } 146 | } 147 | } 148 | 149 | public function send($msg) { 150 | $this->log->log("SEND: $msg", XMPPHP_Log::LEVEL_VERBOSE); 151 | $msg = new SimpleXMLElement($msg); 152 | #$msg->addAttribute('xmlns', 'jabber:client'); 153 | $this->__sendBody($this->__buildBody($msg), true); 154 | #$this->__parseBuffer(); 155 | } 156 | 157 | public function reset() { 158 | $this->xml_depth = 0; 159 | unset($this->xmlobj); 160 | $this->xmlobj = array(); 161 | $this->setupParser(); 162 | #$this->send($this->stream_start); 163 | $body = $this->__buildBody(); 164 | $body->addAttribute('to', $this->host); 165 | $body->addAttribute('xmpp:restart', 'true', 'urn:xmpp:xbosh'); 166 | $buff = ""; 167 | $response = $this->__sendBody($body); 168 | $this->been_reset = true; 169 | xml_parse($this->parser, $buff, false); 170 | } 171 | 172 | public function loadSession() { 173 | if (isset($_SESSION['XMPPHP_BOSH_RID'])) 174 | $this->rid = $_SESSION['XMPPHP_BOSH_RID']; 175 | if (isset($_SESSION['XMPPHP_BOSH_SID'])) 176 | $this->sid = $_SESSION['XMPPHP_BOSH_SID']; 177 | if (isset($_SESSION['XMPPHP_BOSH_authed'])) 178 | $this->authed = $_SESSION['XMPPHP_BOSH_authed']; 179 | if (isset($_SESSION['XMPPHP_BOSH_jid'])) 180 | $this->jid = $_SESSION['XMPPHP_BOSH_jid']; 181 | if (isset($_SESSION['XMPPHP_BOSH_fulljid'])) 182 | $this->fulljid = $_SESSION['XMPPHP_BOSH_fulljid']; 183 | } 184 | 185 | public function saveSession() { 186 | $_SESSION['XMPPHP_BOSH_RID'] = (string) $this->rid; 187 | $_SESSION['XMPPHP_BOSH_SID'] = (string) $this->sid; 188 | $_SESSION['XMPPHP_BOSH_authed'] = (boolean) $this->authed; 189 | $_SESSION['XMPPHP_BOSH_jid'] = (string) $this->jid; 190 | $_SESSION['XMPPHP_BOSH_fulljid'] = (string) $this->fulljid; 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /XMPPHP/Exception.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 28 | */ 29 | 30 | /** 31 | * XMPPHP Exception 32 | * 33 | * @category xmpphp 34 | * @package XMPPHP 35 | * @author Nathanael C. Fritz 36 | * @author Stephan Wentz 37 | * @author Michael Garvin 38 | * @copyright 2008 Nathanael C. Fritz 39 | * @version $Id$ 40 | */ 41 | class XMPPHP_Exception extends Exception { 42 | 43 | } 44 | -------------------------------------------------------------------------------- /XMPPHP/Log.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 28 | */ 29 | 30 | /** 31 | * XMPPHP Log 32 | * 33 | * @package XMPPHP 34 | * @author Nathanael C. Fritz 35 | * @author Stephan Wentz 36 | * @author Michael Garvin 37 | * @copyright 2008 Nathanael C. Fritz 38 | * @version $Id$ 39 | */ 40 | class XMPPHP_Log { 41 | const LEVEL_ERROR = 0; 42 | const LEVEL_WARNING = 1; 43 | const LEVEL_INFO = 2; 44 | const LEVEL_DEBUG = 3; 45 | const LEVEL_VERBOSE = 4; 46 | 47 | /** 48 | * @var array 49 | */ 50 | protected $data = array(); 51 | /** 52 | * @var array 53 | */ 54 | protected $names = array('ERROR', 'WARNING', 'INFO', 'DEBUG', 'VERBOSE'); 55 | /** 56 | * @var integer 57 | */ 58 | protected $runlevel; 59 | /** 60 | * @var boolean 61 | */ 62 | protected $printout; 63 | 64 | /** 65 | * Constructor 66 | * 67 | * @param boolean $printout 68 | * @param string $runlevel 69 | */ 70 | public function __construct($printout = false, $runlevel = self::LEVEL_INFO) { 71 | $this->printout = (boolean) $printout; 72 | $this->runlevel = (int) $runlevel; 73 | } 74 | 75 | /** 76 | * Add a message to the log data array 77 | * If printout in this instance is set to true, directly output the message 78 | * 79 | * @param string $msg 80 | * @param integer $runlevel 81 | */ 82 | public function log($msg, $runlevel = self::LEVEL_INFO) { 83 | $time = time(); 84 | #$this->data[] = array($this->runlevel, $msg, $time); 85 | if ($this->printout and $runlevel <= $this->runlevel) { 86 | $this->writeLine($msg, $runlevel, $time); 87 | } 88 | } 89 | 90 | /** 91 | * Output the complete log. 92 | * Log will be cleared if $clear = true 93 | * 94 | * @param boolean $clear 95 | * @param integer $runlevel 96 | */ 97 | public function printout($clear = true, $runlevel = null) { 98 | if ($runlevel === null) { 99 | $runlevel = $this->runlevel; 100 | } 101 | foreach ($this->data as $data) { 102 | if ($runlevel <= $data[0]) { 103 | $this->writeLine($data[1], $runlevel, $data[2]); 104 | } 105 | } 106 | if ($clear) { 107 | $this->data = array(); 108 | } 109 | } 110 | 111 | protected function writeLine($msg, $runlevel, $time) { 112 | //echo date('Y-m-d H:i:s', $time)." [".$this->names[$runlevel]."]: ".$msg."\n"; 113 | echo $time . " [" . $this->names[$runlevel] . "]: " . $msg . "\n"; 114 | flush(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /XMPPHP/Roster.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 28 | */ 29 | 30 | /** 31 | * XMPPHP Roster Object 32 | * 33 | * @category xmpphp 34 | * @package XMPPHP 35 | * @author Nathanael C. Fritz 36 | * @author Stephan Wentz 37 | * @author Michael Garvin 38 | * @copyright 2008 Nathanael C. Fritz 39 | * @version $Id$ 40 | */ 41 | class Roster { 42 | 43 | /** 44 | * Roster array, handles contacts and presence. Indexed by jid. 45 | * Contains array with potentially two indexes 'contact' and 'presence' 46 | * @var array 47 | */ 48 | protected $roster_array = array(); 49 | 50 | /** 51 | * Constructor 52 | * 53 | */ 54 | public function __construct($roster_array = array()) { 55 | if ($this->verifyRoster($roster_array)) { 56 | $this->roster_array = $roster_array; //Allow for prepopulation with existing roster 57 | } else { 58 | $this->roster_array = array(); 59 | } 60 | } 61 | 62 | /** 63 | * 64 | * Check that a given roster array is of a valid structure (empty is still valid) 65 | * 66 | * @param array $roster_array 67 | */ 68 | protected function verifyRoster($roster_array) { 69 | #TODO once we know *what* a valid roster array looks like 70 | return True; 71 | } 72 | 73 | /** 74 | * 75 | * Add given contact to roster 76 | * 77 | * @param string $jid 78 | * @param string $subscription 79 | * @param string $name 80 | * @param array $groups 81 | */ 82 | public function addContact($jid, $subscription, $name='', $groups=array()) { 83 | $contact = array('jid' => $jid, 'subscription' => $subscription, 'name' => $name, 'groups' => $groups); 84 | if ($this->isContact($jid)) { 85 | $this->roster_array[$jid]['contact'] = $contact; 86 | } else { 87 | $this->roster_array[$jid] = array('contact' => $contact); 88 | } 89 | } 90 | 91 | /** 92 | * 93 | * Retrieve contact via jid 94 | * 95 | * @param string $jid 96 | */ 97 | public function getContact($jid) { 98 | if ($this->isContact($jid)) { 99 | return $this->roster_array[$jid]['contact']; 100 | } 101 | } 102 | 103 | /** 104 | * 105 | * Discover if a contact exists in the roster via jid 106 | * 107 | * @param string $jid 108 | */ 109 | public function isContact($jid) { 110 | return (array_key_exists($jid, $this->roster_array)); 111 | } 112 | 113 | /** 114 | * 115 | * Set presence 116 | * 117 | * @param string $presence 118 | * @param integer $priority 119 | * @param string $show 120 | * @param string $status 121 | */ 122 | public function setPresence($presence, $priority, $show, $status) { 123 | list($jid, $resource) = split("/", $presence); 124 | if ($show != 'unavailable') { 125 | if (!$this->isContact($jid)) { 126 | $this->addContact($jid, 'not-in-roster'); 127 | } 128 | $resource = $resource ? $resource : ''; 129 | $this->roster_array[$jid]['presence'][$resource] = array('priority' => $priority, 'show' => $show, 'status' => $status); 130 | } else { //Nuke unavailable resources to save memory 131 | unset($this->roster_array[$jid]['resource'][$resource]); 132 | } 133 | } 134 | 135 | /* 136 | * 137 | * Return best presence for jid 138 | * 139 | * @param string $jid 140 | */ 141 | 142 | public function getPresence($jid) { 143 | $split = split("/", $jid); 144 | $jid = $split[0]; 145 | if ($this->isContact($jid)) { 146 | $current = array('resource' => '', 'active' => '', 'priority' => -129, 'show' => '', 'status' => ''); //Priorities can only be -128 = 127 147 | foreach ($this->roster_array[$jid]['presence'] as $resource => $presence) { 148 | //Highest available priority or just highest priority 149 | if ($presence['priority'] > $current['priority'] and (($presence['show'] == "chat" or $presence['show'] == "available") or ($current['show'] != "chat" or $current['show'] != "available"))) { 150 | $current = $presence; 151 | $current['resource'] = $resource; 152 | } 153 | } 154 | return $current; 155 | } 156 | } 157 | 158 | /** 159 | * 160 | * Get roster 161 | * 162 | */ 163 | public function getRoster() { 164 | return $this->roster_array; 165 | } 166 | 167 | } 168 | 169 | ?> 170 | -------------------------------------------------------------------------------- /XMPPHP/XMLObj.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 28 | */ 29 | 30 | /** 31 | * XMPPHP XML Object 32 | * 33 | * @category xmpphp 34 | * @package XMPPHP 35 | * @author Nathanael C. Fritz 36 | * @author Stephan Wentz 37 | * @author Michael Garvin 38 | * @copyright 2008 Nathanael C. Fritz 39 | * @version $Id$ 40 | */ 41 | class XMPPHP_XMLObj { 42 | 43 | /** 44 | * Tag name 45 | * 46 | * @var string 47 | */ 48 | public $name; 49 | /** 50 | * Namespace 51 | * 52 | * @var string 53 | */ 54 | public $ns; 55 | /** 56 | * Attributes 57 | * 58 | * @var array 59 | */ 60 | public $attrs = array(); 61 | /** 62 | * Subs? 63 | * 64 | * @var array 65 | */ 66 | public $subs = array(); 67 | /** 68 | * Node data 69 | * 70 | * @var string 71 | */ 72 | public $data = ''; 73 | 74 | /** 75 | * Constructor 76 | * 77 | * @param string $name 78 | * @param string $ns 79 | * @param array $attrs 80 | * @param string $data 81 | */ 82 | public function __construct($name, $ns = '', $attrs = array(), $data = '') { 83 | $this->name = strtolower($name); 84 | $this->ns = $ns; 85 | if (is_array($attrs) && count($attrs)) { 86 | foreach ($attrs as $key => $value) { 87 | $this->attrs[strtolower($key)] = $value; 88 | } 89 | } 90 | $this->data = $data; 91 | } 92 | 93 | /** 94 | * Dump this XML Object to output. 95 | * 96 | * @param integer $depth 97 | */ 98 | public function printObj($depth = 0) { 99 | print str_repeat("\t", $depth) . $this->name . " " . $this->ns . ' ' . $this->data; 100 | print "\n"; 101 | foreach ($this->subs as $sub) { 102 | $sub->printObj($depth + 1); 103 | } 104 | } 105 | 106 | /** 107 | * Return this XML Object in xml notation 108 | * 109 | * @param string $str 110 | */ 111 | public function toString($str = '') { 112 | $str .= "<{$this->name} xmlns='{$this->ns}' "; 113 | foreach ($this->attrs as $key => $value) { 114 | if ($key != 'xmlns') { 115 | $value = htmlspecialchars($value); 116 | $str .= "$key='$value' "; 117 | } 118 | } 119 | $str .= ">"; 120 | foreach ($this->subs as $sub) { 121 | $str .= $sub->toString(); 122 | } 123 | $body = htmlspecialchars($this->data); 124 | $str .= "$bodyname}>"; 125 | return $str; 126 | } 127 | 128 | /** 129 | * Has this XML Object the given sub? 130 | * 131 | * @param string $name 132 | * @return boolean 133 | */ 134 | public function hasSub($name, $ns = null) { 135 | foreach ($this->subs as $sub) { 136 | if (($name == "*" or $sub->name == $name) and ($ns == null or $sub->ns == $ns)) 137 | return true; 138 | } 139 | return false; 140 | } 141 | 142 | /** 143 | * Return a sub 144 | * 145 | * @param string $name 146 | * @param string $attrs 147 | * @param string $ns 148 | */ 149 | public function sub($name, $attrs = null, $ns = null) { 150 | #TODO attrs is ignored 151 | foreach ($this->subs as $sub) { 152 | if ($sub->name == $name and ($ns == null or $sub->ns == $ns)) { 153 | return $sub; 154 | } 155 | } 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /XMPPHP/XMLStream.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 28 | */ 29 | /** XMPPHP_Exception */ 30 | require_once dirname(__FILE__) . '/Exception.php'; 31 | 32 | /** XMPPHP_XMLObj */ 33 | require_once dirname(__FILE__) . '/XMLObj.php'; 34 | 35 | /** XMPPHP_Log */ 36 | require_once dirname(__FILE__) . '/Log.php'; 37 | 38 | /** 39 | * XMPPHP XML Stream 40 | * 41 | * @category xmpphp 42 | * @package XMPPHP 43 | * @author Nathanael C. Fritz 44 | * @author Stephan Wentz 45 | * @author Michael Garvin 46 | * @copyright 2008 Nathanael C. Fritz 47 | * @version $Id$ 48 | */ 49 | class XMPPHP_XMLStream { 50 | 51 | /** 52 | * @var resource 53 | */ 54 | protected $socket; 55 | /** 56 | * @var resource 57 | */ 58 | protected $parser; 59 | /** 60 | * @var string 61 | */ 62 | protected $buffer; 63 | /** 64 | * @var integer 65 | */ 66 | protected $xml_depth = 0; 67 | /** 68 | * @var string 69 | */ 70 | protected $host; 71 | /** 72 | * @var integer 73 | */ 74 | protected $port; 75 | /** 76 | * @var string 77 | */ 78 | protected $stream_start = ''; 79 | /** 80 | * @var string 81 | */ 82 | protected $stream_end = ''; 83 | /** 84 | * @var boolean 85 | */ 86 | protected $disconnected = false; 87 | /** 88 | * @var boolean 89 | */ 90 | protected $sent_disconnect = false; 91 | /** 92 | * @var array 93 | */ 94 | protected $ns_map = array(); 95 | /** 96 | * @var array 97 | */ 98 | protected $current_ns = array(); 99 | /** 100 | * @var array 101 | */ 102 | protected $xmlobj = null; 103 | /** 104 | * @var array 105 | */ 106 | protected $nshandlers = array(); 107 | /** 108 | * @var array 109 | */ 110 | protected $xpathhandlers = array(); 111 | /** 112 | * @var array 113 | */ 114 | protected $idhandlers = array(); 115 | /** 116 | * @var array 117 | */ 118 | protected $eventhandlers = array(); 119 | /** 120 | * @var integer 121 | */ 122 | protected $lastid = 0; 123 | /** 124 | * @var string 125 | */ 126 | protected $default_ns; 127 | /** 128 | * @var string 129 | */ 130 | protected $until = ''; 131 | /** 132 | * @var string 133 | */ 134 | protected $until_count = ''; 135 | /** 136 | * @var array 137 | */ 138 | protected $until_happened = false; 139 | /** 140 | * @var array 141 | */ 142 | protected $until_payload = array(); 143 | /** 144 | * @var XMPPHP_Log 145 | */ 146 | protected $log; 147 | /** 148 | * @var boolean 149 | */ 150 | protected $reconnect = true; 151 | /** 152 | * @var boolean 153 | */ 154 | protected $been_reset = false; 155 | /** 156 | * @var boolean 157 | */ 158 | protected $is_server; 159 | /** 160 | * @var float 161 | */ 162 | protected $last_send = 0; 163 | /** 164 | * @var boolean 165 | */ 166 | protected $use_ssl = false; 167 | /** 168 | * @var integer 169 | */ 170 | protected $reconnectTimeout = 30; 171 | 172 | /** 173 | * Constructor 174 | * 175 | * @param string $host 176 | * @param string $port 177 | * @param boolean $printlog 178 | * @param string $loglevel 179 | * @param boolean $is_server 180 | */ 181 | public function __construct($host = null, $port = null, $printlog = false, $loglevel = null, $is_server = false) { 182 | $this->reconnect = !$is_server; 183 | $this->is_server = $is_server; 184 | $this->host = $host; 185 | $this->port = $port; 186 | $this->setupParser(); 187 | $this->log = new XMPPHP_Log($printlog, $loglevel); 188 | } 189 | 190 | /** 191 | * Destructor 192 | * Cleanup connection 193 | */ 194 | public function __destruct() { 195 | if (!$this->disconnected && $this->socket) { 196 | $this->disconnect(); 197 | } 198 | } 199 | 200 | /** 201 | * Return the log instance 202 | * 203 | * @return XMPPHP_Log 204 | */ 205 | public function getLog() { 206 | return $this->log; 207 | } 208 | 209 | /** 210 | * Get next ID 211 | * 212 | * @return integer 213 | */ 214 | public function getId() { 215 | $this->lastid++; 216 | return $this->lastid; 217 | } 218 | 219 | /** 220 | * Set SSL 221 | * 222 | * @return integer 223 | */ 224 | public function useSSL($use=true) { 225 | $this->use_ssl = $use; 226 | } 227 | 228 | /** 229 | * Add ID Handler 230 | * 231 | * @param integer $id 232 | * @param string $pointer 233 | * @param string $obj 234 | */ 235 | public function addIdHandler($id, $pointer, $obj = null) { 236 | $this->idhandlers[$id] = array($pointer, $obj); 237 | } 238 | 239 | /** 240 | * Add Handler 241 | * 242 | * @param string $name 243 | * @param string $ns 244 | * @param string $pointer 245 | * @param string $obj 246 | * @param integer $depth 247 | */ 248 | public function addHandler($name, $ns, $pointer, $obj = null, $depth = 1) { 249 | #TODO deprication warning 250 | $this->nshandlers[] = array($name, $ns, $pointer, $obj, $depth); 251 | } 252 | 253 | /** 254 | * Add XPath Handler 255 | * 256 | * @param string $xpath 257 | * @param string $pointer 258 | * @param 259 | */ 260 | public function addXPathHandler($xpath, $pointer, $obj = null) { 261 | if (preg_match_all("/\(?{[^\}]+}\)?(\/?)[^\/]+/", $xpath, $regs)) { 262 | $ns_tags = $regs[0]; 263 | } else { 264 | $ns_tags = array($xpath); 265 | } 266 | foreach ($ns_tags as $ns_tag) { 267 | list($l, $r) = explode("}", $ns_tag); 268 | if ($r != null) { 269 | $xpart = array(substr($l, 1), $r); 270 | } else { 271 | $xpart = array(null, $l); 272 | } 273 | $xpath_array[] = $xpart; 274 | } 275 | $this->xpathhandlers[] = array($xpath_array, $pointer, $obj); 276 | } 277 | 278 | /** 279 | * Add Event Handler 280 | * 281 | * @param integer $id 282 | * @param string $pointer 283 | * @param string $obj 284 | */ 285 | public function addEventHandler($name, $pointer, $obj) { 286 | $this->eventhandlers[] = array($name, $pointer, $obj); 287 | } 288 | 289 | /** 290 | * Connect to XMPP Host 291 | * 292 | * @param integer $timeout 293 | * @param boolean $persistent 294 | * @param boolean $sendinit 295 | */ 296 | public function connect($timeout = 30, $persistent = false, $sendinit = true) { 297 | $this->sent_disconnect = false; 298 | $starttime = time(); 299 | 300 | do { 301 | $this->disconnected = false; 302 | $this->sent_disconnect = false; 303 | if ($persistent) { 304 | $conflag = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT; 305 | } else { 306 | $conflag = STREAM_CLIENT_CONNECT; 307 | } 308 | $conntype = 'tcp'; 309 | if ($this->use_ssl) 310 | $conntype = 'ssl'; 311 | $this->log->log("Connecting to $conntype://{$this->host}:{$this->port}"); 312 | try { 313 | $this->socket = @stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag); 314 | } catch (Exception $e) { 315 | throw new XMPPHP_Exception($e->getMessage()); 316 | } 317 | if (!$this->socket) { 318 | $this->log->log("Could not connect.", XMPPHP_Log::LEVEL_ERROR); 319 | $this->disconnected = true; 320 | # Take it easy for a few seconds 321 | sleep(min($timeout, 5)); 322 | } 323 | } while (!$this->socket && (time() - $starttime) < $timeout); 324 | 325 | if ($this->socket) { 326 | stream_set_blocking($this->socket, 1); 327 | if ($sendinit) 328 | $this->send($this->stream_start); 329 | } else { 330 | throw new XMPPHP_Exception("Could not connect before timeout."); 331 | } 332 | } 333 | 334 | /** 335 | * Reconnect XMPP Host 336 | */ 337 | public function doReconnect() { 338 | if (!$this->is_server) { 339 | $this->log->log("Reconnecting ($this->reconnectTimeout)...", XMPPHP_Log::LEVEL_WARNING); 340 | $this->connect($this->reconnectTimeout, false, false); 341 | $this->reset(); 342 | $this->event('reconnect'); 343 | } 344 | } 345 | 346 | public function setReconnectTimeout($timeout) { 347 | $this->reconnectTimeout = $timeout; 348 | } 349 | 350 | /** 351 | * Disconnect from XMPP Host 352 | */ 353 | public function disconnect() { 354 | $this->log->log("Disconnecting...", XMPPHP_Log::LEVEL_VERBOSE); 355 | if (false == (bool) $this->socket) { 356 | return; 357 | } 358 | $this->reconnect = false; 359 | $this->send($this->stream_end); 360 | $this->sent_disconnect = true; 361 | $this->processUntil('end_stream', 5); 362 | $this->disconnected = true; 363 | } 364 | 365 | /** 366 | * Are we are disconnected? 367 | * 368 | * @return boolean 369 | */ 370 | public function isDisconnected() { 371 | return $this->disconnected; 372 | } 373 | 374 | /** 375 | * Core reading tool 376 | * 0 -> only read if data is immediately ready 377 | * NULL -> wait forever and ever 378 | * integer -> process for this amount of time 379 | */ 380 | private function __process($maximum=5) { 381 | 382 | $remaining = $maximum; 383 | 384 | do { 385 | $starttime = (microtime(true) * 1000000); 386 | $read = array($this->socket); 387 | $write = array(); 388 | $except = array(); 389 | if (is_null($maximum)) { 390 | $secs = NULL; 391 | $usecs = NULL; 392 | } else if ($maximum == 0) { 393 | $secs = 0; 394 | $usecs = 0; 395 | } else { 396 | $usecs = $remaining % 1000000; 397 | $secs = floor(($remaining - $usecs) / 1000000); 398 | } 399 | $updated = @stream_select($read, $write, $except, $secs, $usecs); 400 | if ($updated === false) { 401 | $this->log->log("Error on stream_select()", XMPPHP_Log::LEVEL_VERBOSE); 402 | if ($this->reconnect) { 403 | $this->doReconnect(); 404 | } else { 405 | fclose($this->socket); 406 | $this->socket = NULL; 407 | return false; 408 | } 409 | } else if ($updated > 0) { 410 | # XXX: Is this big enough? 411 | $buff = @fread($this->socket, 4096); 412 | if (!$buff) { 413 | if ($this->reconnect) { 414 | $this->doReconnect(); 415 | } else { 416 | fclose($this->socket); 417 | $this->socket = NULL; 418 | return false; 419 | } 420 | } 421 | $this->log->log("RECV: $buff", XMPPHP_Log::LEVEL_VERBOSE); 422 | xml_parse($this->parser, $buff, false); 423 | } else { 424 | # $updated == 0 means no changes during timeout. 425 | } 426 | $endtime = (microtime(true) * 1000000); 427 | $time_past = $endtime - $starttime; 428 | $remaining = $remaining - $time_past; 429 | } while (is_null($maximum) || $remaining > 0); 430 | return true; 431 | } 432 | 433 | /** 434 | * Process 435 | * 436 | * @return string 437 | */ 438 | public function process() { 439 | $this->__process(NULL); 440 | } 441 | 442 | /** 443 | * Process until a timeout occurs 444 | * 445 | * @param integer $timeout 446 | * @return string 447 | */ 448 | public function processTime($timeout=NULL) { 449 | if (is_null($timeout)) { 450 | return $this->__process(NULL); 451 | } else { 452 | return $this->__process($timeout * 1000000); 453 | } 454 | } 455 | 456 | /** 457 | * Process until a specified event or a timeout occurs 458 | * 459 | * @param string|array $event 460 | * @param integer $timeout 461 | * @return string 462 | */ 463 | public function processUntil($event, $timeout=-1) { 464 | $start = time(); 465 | if (!is_array($event)) 466 | $event = array($event); 467 | $this->until[] = $event; 468 | end($this->until); 469 | $event_key = key($this->until); 470 | reset($this->until); 471 | $this->until_count[$event_key] = 0; 472 | $updated = ''; 473 | while (!$this->disconnected and $this->until_count[$event_key] < 1 and (time() - $start < $timeout or $timeout == -1)) { 474 | $this->__process(); 475 | } 476 | if (array_key_exists($event_key, $this->until_payload)) { 477 | $payload = $this->until_payload[$event_key]; 478 | unset($this->until_payload[$event_key]); 479 | unset($this->until_count[$event_key]); 480 | unset($this->until[$event_key]); 481 | } else { 482 | $payload = array(); 483 | } 484 | return $payload; 485 | } 486 | 487 | /** 488 | * Obsolete? 489 | */ 490 | public function Xapply_socket($socket) { 491 | $this->socket = $socket; 492 | } 493 | 494 | /** 495 | * XML start callback 496 | * 497 | * @see xml_set_element_handler 498 | * 499 | * @param resource $parser 500 | * @param string $name 501 | */ 502 | public function startXML($parser, $name, $attr) { 503 | if ($this->been_reset) { 504 | $this->been_reset = false; 505 | $this->xml_depth = 0; 506 | } 507 | $this->xml_depth++; 508 | if (array_key_exists('XMLNS', $attr)) { 509 | $this->current_ns[$this->xml_depth] = $attr['XMLNS']; 510 | } else { 511 | $this->current_ns[$this->xml_depth] = $this->current_ns[$this->xml_depth - 1]; 512 | if (!$this->current_ns[$this->xml_depth]) 513 | $this->current_ns[$this->xml_depth] = $this->default_ns; 514 | } 515 | $ns = $this->current_ns[$this->xml_depth]; 516 | foreach ($attr as $key => $value) { 517 | if (strstr($key, ":")) { 518 | $key = explode(':', $key); 519 | $key = $key[1]; 520 | $this->ns_map[$key] = $value; 521 | } 522 | } 523 | if (!strstr($name, ":") === false) { 524 | $name = explode(':', $name); 525 | $ns = $this->ns_map[$name[0]]; 526 | $name = $name[1]; 527 | } 528 | $obj = new XMPPHP_XMLObj($name, $ns, $attr); 529 | if ($this->xml_depth > 1) { 530 | $this->xmlobj[$this->xml_depth - 1]->subs[] = $obj; 531 | } 532 | $this->xmlobj[$this->xml_depth] = $obj; 533 | } 534 | 535 | /** 536 | * XML end callback 537 | * 538 | * @see xml_set_element_handler 539 | * 540 | * @param resource $parser 541 | * @param string $name 542 | */ 543 | public function endXML($parser, $name) { 544 | #$this->log->log("Ending $name", XMPPHP_Log::LEVEL_DEBUG); 545 | #print "$name\n"; 546 | if ($this->been_reset) { 547 | $this->been_reset = false; 548 | $this->xml_depth = 0; 549 | } 550 | $this->xml_depth--; 551 | if ($this->xml_depth == 1) { 552 | #clean-up old objects 553 | #$found = false; #FIXME This didn't appear to be in use --Gar 554 | foreach ($this->xpathhandlers as $handler) { 555 | if (is_array($this->xmlobj) && array_key_exists(2, $this->xmlobj)) { 556 | $searchxml = $this->xmlobj[2]; 557 | $nstag = array_shift($handler[0]); 558 | if (($nstag[0] == null or $searchxml->ns == $nstag[0]) and ($nstag[1] == "*" or $nstag[1] == $searchxml->name)) { 559 | foreach ($handler[0] as $nstag) { 560 | if ($searchxml !== null and $searchxml->hasSub($nstag[1], $ns = $nstag[0])) { 561 | $searchxml = $searchxml->sub($nstag[1], $ns = $nstag[0]); 562 | } else { 563 | $searchxml = null; 564 | break; 565 | } 566 | } 567 | if ($searchxml !== null) { 568 | if ($handler[2] === null) 569 | $handler[2] = $this; 570 | $this->log->log("Calling {$handler[1]}", XMPPHP_Log::LEVEL_DEBUG); 571 | $handler[2]->$handler[1]($this->xmlobj[2]); 572 | } 573 | } 574 | } 575 | } 576 | foreach ($this->nshandlers as $handler) { 577 | if ($handler[4] != 1 and array_key_exists(2, $this->xmlobj) and $this->xmlobj[2]->hasSub($handler[0])) { 578 | $searchxml = $this->xmlobj[2]->sub($handler[0]); 579 | } elseif (is_array($this->xmlobj) and array_key_exists(2, $this->xmlobj)) { 580 | $searchxml = $this->xmlobj[2]; 581 | } 582 | if ($searchxml !== null and $searchxml->name == $handler[0] and ($searchxml->ns == $handler[1] or (!$handler[1] and $searchxml->ns == $this->default_ns))) { 583 | if ($handler[3] === null) 584 | $handler[3] = $this; 585 | $this->log->log("Calling {$handler[2]}", XMPPHP_Log::LEVEL_DEBUG); 586 | $handler[3]->$handler[2]($this->xmlobj[2]); 587 | } 588 | } 589 | foreach ($this->idhandlers as $id => $handler) { 590 | if (array_key_exists('id', $this->xmlobj[2]->attrs) and $this->xmlobj[2]->attrs['id'] == $id) { 591 | if ($handler[1] === null) 592 | $handler[1] = $this; 593 | $handler[1]->$handler[0]($this->xmlobj[2]); 594 | #id handlers are only used once 595 | unset($this->idhandlers[$id]); 596 | break; 597 | } 598 | } 599 | if (is_array($this->xmlobj)) { 600 | $this->xmlobj = array_slice($this->xmlobj, 0, 1); 601 | if (isset($this->xmlobj[0]) && $this->xmlobj[0] instanceof XMPPHP_XMLObj) { 602 | $this->xmlobj[0]->subs = null; 603 | } 604 | } 605 | unset($this->xmlobj[2]); 606 | } 607 | if ($this->xml_depth == 0 and !$this->been_reset) { 608 | if (!$this->disconnected) { 609 | if (!$this->sent_disconnect) { 610 | $this->send($this->stream_end); 611 | } 612 | $this->disconnected = true; 613 | $this->sent_disconnect = true; 614 | fclose($this->socket); 615 | if ($this->reconnect) { 616 | $this->doReconnect(); 617 | } 618 | } 619 | $this->event('end_stream'); 620 | } 621 | } 622 | 623 | /** 624 | * XML character callback 625 | * @see xml_set_character_data_handler 626 | * 627 | * @param resource $parser 628 | * @param string $data 629 | */ 630 | public function charXML($parser, $data) { 631 | if (array_key_exists($this->xml_depth, $this->xmlobj)) { 632 | $this->xmlobj[$this->xml_depth]->data .= $data; 633 | } 634 | } 635 | 636 | /** 637 | * Event? 638 | * 639 | * @param string $name 640 | * @param string $payload 641 | */ 642 | public function event($name, $payload = null) { 643 | $this->log->log("EVENT: $name", XMPPHP_Log::LEVEL_DEBUG); 644 | foreach ($this->eventhandlers as $handler) { 645 | if ($name == $handler[0]) { 646 | if ($handler[2] === null) { 647 | $handler[2] = $this; 648 | } 649 | $handler[2]->$handler[1]($payload); 650 | } 651 | } 652 | foreach ($this->until as $key => $until) { 653 | if (is_array($until)) { 654 | if (in_array($name, $until)) { 655 | $this->until_payload[$key][] = array($name, $payload); 656 | if (!isset($this->until_count[$key])) { 657 | $this->until_count[$key] = 0; 658 | } 659 | $this->until_count[$key] += 1; 660 | #$this->until[$key] = false; 661 | } 662 | } 663 | } 664 | } 665 | 666 | /** 667 | * Read from socket 668 | */ 669 | public function read() { 670 | $buff = @fread($this->socket, 1024); 671 | if (!$buff) { 672 | if ($this->reconnect) { 673 | $this->doReconnect(); 674 | } else { 675 | fclose($this->socket); 676 | return false; 677 | } 678 | } 679 | $this->log->log("RECV: $buff", XMPPHP_Log::LEVEL_VERBOSE); 680 | xml_parse($this->parser, $buff, false); 681 | } 682 | 683 | /** 684 | * Send to socket 685 | * 686 | * @param string $msg 687 | */ 688 | public function send($msg, $timeout=NULL) { 689 | 690 | if (is_null($timeout)) { 691 | $secs = NULL; 692 | $usecs = NULL; 693 | } else if ($timeout == 0) { 694 | $secs = 0; 695 | $usecs = 0; 696 | } else { 697 | $maximum = $timeout * 1000000; 698 | $usecs = $maximum % 1000000; 699 | $secs = floor(($maximum - $usecs) / 1000000); 700 | } 701 | 702 | $read = array(); 703 | $write = array($this->socket); 704 | $except = array(); 705 | 706 | $select = @stream_select($read, $write, $except, $secs, $usecs); 707 | 708 | if ($select === False) { 709 | $this->log->log("ERROR sending message; reconnecting."); 710 | $this->doReconnect(); 711 | # TODO: retry send here 712 | return false; 713 | } elseif ($select > 0) { 714 | $this->log->log("Socket is ready; send it.", XMPPHP_Log::LEVEL_VERBOSE); 715 | } else { 716 | $this->log->log("Socket is not ready; break.", XMPPHP_Log::LEVEL_ERROR); 717 | return false; 718 | } 719 | 720 | $sentbytes = @fwrite($this->socket, $msg); 721 | $this->log->log("SENT: " . mb_substr($msg, 0, $sentbytes, '8bit'), XMPPHP_Log::LEVEL_VERBOSE); 722 | if ($sentbytes === FALSE) { 723 | $this->log->log("ERROR sending message; reconnecting.", XMPPHP_Log::LEVEL_ERROR); 724 | $this->doReconnect(); 725 | return false; 726 | } 727 | $this->log->log("Successfully sent $sentbytes bytes.", XMPPHP_Log::LEVEL_VERBOSE); 728 | return $sentbytes; 729 | } 730 | 731 | public function time() { 732 | list($usec, $sec) = explode(" ", microtime()); 733 | return (float) $sec + (float) $usec; 734 | } 735 | 736 | /** 737 | * Reset connection 738 | */ 739 | public function reset() { 740 | $this->xml_depth = 0; 741 | unset($this->xmlobj); 742 | $this->xmlobj = array(); 743 | $this->setupParser(); 744 | if (!$this->is_server) { 745 | $this->send($this->stream_start); 746 | } 747 | $this->been_reset = true; 748 | } 749 | 750 | /** 751 | * Setup the XML parser 752 | */ 753 | public function setupParser() { 754 | $this->parser = xml_parser_create('UTF-8'); 755 | xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1); 756 | xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8'); 757 | xml_set_object($this->parser, $this); 758 | xml_set_element_handler($this->parser, 'startXML', 'endXML'); 759 | xml_set_character_data_handler($this->parser, 'charXML'); 760 | } 761 | 762 | public function readyToProcess() { 763 | $read = array($this->socket); 764 | $write = array(); 765 | $except = array(); 766 | $updated = @stream_select($read, $write, $except, 0); 767 | return (($updated !== false) && ($updated > 0)); 768 | } 769 | 770 | } 771 | -------------------------------------------------------------------------------- /XMPPHP/XMPP.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 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 39 | * @author Stephan Wentz 40 | * @author Michael Garvin 41 | * @copyright 2008 Nathanael C. Fritz 42 | * @version $Id$ 43 | */ 44 | class XMPPHP_XMPP extends XMPPHP_XMLStream { 45 | 46 | /** 47 | * @var string 48 | */ 49 | public $server; 50 | /** 51 | * @var string 52 | */ 53 | public $user; 54 | /** 55 | * @var string 56 | */ 57 | protected $password; 58 | /** 59 | * @var string 60 | */ 61 | protected $resource; 62 | /** 63 | * @var string 64 | */ 65 | protected $fulljid; 66 | /** 67 | * @var string 68 | */ 69 | protected $basejid; 70 | /** 71 | * @var boolean 72 | */ 73 | protected $authed = false; 74 | protected $session_started = false; 75 | /** 76 | * @var boolean 77 | */ 78 | protected $auto_subscribe = false; 79 | /** 80 | * @var boolean 81 | */ 82 | protected $use_encryption = true; 83 | /** 84 | * @var boolean 85 | */ 86 | public $track_presence = true; 87 | /** 88 | * @var object 89 | */ 90 | public $roster; 91 | /** 92 | * @var array supported auth mechanisms 93 | */ 94 | protected $auth_mechanism_supported = array('PLAIN', 'DIGEST-MD5'); 95 | /** 96 | * @var string default auth mechanism 97 | */ 98 | protected $auth_mechanism_default = 'PLAIN'; 99 | /** 100 | * @var string prefered auth mechanism 101 | */ 102 | protected $auth_mechanism_preferred = 'DIGEST-MD5'; 103 | 104 | /** 105 | * Constructor 106 | * 107 | * @param string $host 108 | * @param integer $port 109 | * @param string $user 110 | * @param string $password 111 | * @param string $resource 112 | * @param string $server 113 | * @param boolean $printlog 114 | * @param string $loglevel 115 | */ 116 | public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null) { 117 | parent::__construct($host, $port, $printlog, $loglevel); 118 | 119 | $this->user = $user; 120 | $this->password = $password; 121 | $this->resource = $resource; 122 | if (!$server) 123 | $server = $host; 124 | $this->server = $server; 125 | $this->basejid = $this->user . '@' . $this->host; 126 | 127 | $this->roster = new Roster(); 128 | $this->track_presence = true; 129 | 130 | $this->stream_start = ''; 131 | $this->stream_end = ''; 132 | $this->default_ns = 'jabber:client'; 133 | 134 | $this->addXPathHandler('{http://etherx.jabber.org/streams}features', 'features_handler'); 135 | $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}success', 'sasl_success_handler'); 136 | $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}failure', 'sasl_failure_handler'); 137 | $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-tls}proceed', 'tls_proceed_handler'); 138 | $this->addXPathHandler('{jabber:client}message', 'message_handler'); 139 | $this->addXPathHandler('{jabber:client}presence', 'presence_handler'); 140 | $this->addXPathHandler('iq/{jabber:iq:roster}query', 'roster_iq_handler'); 141 | // For DIGEST-MD5 auth : 142 | $this->addXPathHandler('{urn:ietf:params:xml:ns:xmpp-sasl}challenge', 'sasl_challenge_handler'); 143 | } 144 | 145 | /** 146 | * Turn encryption on/ff 147 | * 148 | * @param boolean $useEncryption 149 | */ 150 | public function useEncryption($useEncryption = true) { 151 | $this->use_encryption = $useEncryption; 152 | } 153 | 154 | /** 155 | * Turn on auto-authorization of subscription requests. 156 | * 157 | * @param boolean $autoSubscribe 158 | */ 159 | public function autoSubscribe($autoSubscribe = true) { 160 | $this->auto_subscribe = $autoSubscribe; 161 | } 162 | 163 | /** 164 | * Send XMPP Message 165 | * 166 | * @param string $to 167 | * @param string $body 168 | * @param string $type 169 | * @param string $subject 170 | */ 171 | public function message($to, $body, $type = 'chat', $subject = null, $payload = null) { 172 | if (is_null($type)) { 173 | $type = 'chat'; 174 | } 175 | 176 | $to = htmlspecialchars($to); 177 | $body = htmlspecialchars($body); 178 | $subject = htmlspecialchars($subject); 179 | 180 | $out = "fulljid}\" to=\"$to\" type='$type'>"; 181 | if ($subject) 182 | $out .= "$subject"; 183 | $out .= "$body"; 184 | if ($payload) 185 | $out .= $payload; 186 | $out .= ""; 187 | 188 | $this->send($out); 189 | } 190 | 191 | /** 192 | * Register a new user 193 | * @param string $username 194 | * @param string $password 195 | */ 196 | public function register($username, $password = null){ 197 | if (!isset($password)) $password = $this->genRandomString(15); 198 | $id = 'reg_' . $this->getID(); 199 | $xml = " 200 | 201 | " . $username . " 202 | " . $password . " 203 | 204 | 205 | 206 | "; 207 | $this->addIdHandler($id, 'register_new_user_handler'); 208 | $this->send($xml); 209 | } 210 | 211 | /** 212 | * Handler for new user registration 213 | * @param XML Object $xml 214 | */ 215 | protected function register_new_user_handler($xml){ 216 | switch ($xml->attrs['type']) { 217 | case 'error': 218 | $this->event('new_user_registered', 'error'); 219 | break; 220 | case 'result': 221 | $query = $xml->sub('query'); 222 | $username=''; 223 | $password=''; 224 | if(!is_array($query->subs)) { 225 | foreach ($query->sub as $key => $value) { 226 | switch ($value->name) { 227 | case 'username': 228 | $username = $value->data; 229 | break; 230 | case 'password': 231 | $password = $value->data; 232 | break; 233 | } 234 | } 235 | } 236 | $this->event('new_user_registered', array('jid' => $username . "@{$this->server}", 'password' => $password)); 237 | default: 238 | $this->event('new_user_registered', 'default'); 239 | } 240 | } 241 | 242 | /** 243 | * Set Presence 244 | * 245 | * @param string $status 246 | * @param string $show 247 | * @param string $to 248 | */ 249 | public function presence($status = null, $show = 'available', $to = null, $type='available', $priority=0) { 250 | if ($type == 'available') 251 | $type = ''; 252 | $to = htmlspecialchars($to); 253 | $status = htmlspecialchars($status); 254 | if ($show == 'unavailable') 255 | $type = 'unavailable'; 256 | 257 | $out = "send($out); 276 | } 277 | 278 | /** 279 | * Send Auth request 280 | * 281 | * @param string $jid 282 | */ 283 | public function subscribe($jid) { 284 | $this->send(""); 285 | #$this->send(""); 286 | } 287 | 288 | /** 289 | * Message handler 290 | * 291 | * @param string $xml 292 | */ 293 | public function message_handler($xml) { 294 | if (isset($xml->attrs['type'])) { 295 | $payload['type'] = $xml->attrs['type']; 296 | } else { 297 | $payload['type'] = 'chat'; 298 | } 299 | $payload['from'] = $xml->attrs['from']; 300 | $payload['body'] = $xml->sub('body')->data; 301 | $payload['xml'] = $xml; 302 | $this->log->log("Message: {$xml->sub('body')->data}", XMPPHP_Log::LEVEL_DEBUG); 303 | $this->event('message', $payload); 304 | } 305 | 306 | /** 307 | * Presence handler 308 | * 309 | * @param string $xml 310 | */ 311 | public function presence_handler($xml) { 312 | $payload['type'] = (isset($xml->attrs['type'])) ? $xml->attrs['type'] : 'available'; 313 | $payload['show'] = (isset($xml->sub('show')->data)) ? $xml->sub('show')->data : $payload['type']; 314 | $payload['from'] = $xml->attrs['from']; 315 | $payload['status'] = (isset($xml->sub('status')->data)) ? $xml->sub('status')->data : ''; 316 | $payload['priority'] = (isset($xml->sub('priority')->data)) ? intval($xml->sub('priority')->data) : 0; 317 | $payload['xml'] = $xml; 318 | if ($this->track_presence) { 319 | $this->roster->setPresence($payload['from'], $payload['priority'], $payload['show'], $payload['status']); 320 | } 321 | $this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}", XMPPHP_Log::LEVEL_DEBUG); 322 | if (array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribe') { 323 | if ($this->auto_subscribe) { 324 | $this->send(""); 325 | $this->send(""); 326 | } 327 | $this->event('subscription_requested', $payload); 328 | } elseif (array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribed') { 329 | $this->event('subscription_accepted', $payload); 330 | } else { 331 | $this->event('presence', $payload); 332 | } 333 | } 334 | 335 | /** 336 | * Features handler 337 | * 338 | * @param string $xml 339 | */ 340 | protected function features_handler($xml) { 341 | if ($xml->hasSub('starttls') and $this->use_encryption) { 342 | $this->send(""); 343 | } elseif ($xml->hasSub('bind') and $this->authed) { 344 | $id = $this->getId(); 345 | $this->addIdHandler($id, 'resource_bind_handler'); 346 | $this->send("{$this->resource}"); 347 | } else { 348 | $this->log->log("Attempting Auth..."); 349 | if ($this->password) { 350 | $mechanism = 'PLAIN'; // default; 351 | if ($xml->hasSub('mechanisms') && $xml->sub('mechanisms')->hasSub('mechanism')) { 352 | // Get the list of all available auth mechanism that we can use 353 | $available = array(); 354 | foreach ($xml->sub('mechanisms')->subs as $sub) { 355 | if ($sub->name == 'mechanism') { 356 | if (in_array($sub->data, $this->auth_mechanism_supported)) { 357 | $available[$sub->data] = $sub->data; 358 | } 359 | } 360 | } 361 | if (isset($available[$this->auth_mechanism_preferred])) { 362 | $mechanism = $this->auth_mechanism_preferred; 363 | } else { 364 | // use the first available 365 | $mechanism = reset($available); 366 | } 367 | $this->log->log("Trying $mechanism (available : " . implode(',', $available) . ')'); 368 | } 369 | switch ($mechanism) { 370 | case 'PLAIN': 371 | $this->send("" . base64_encode("\x00" . $this->user . "\x00" . $this->password) . ""); 372 | break; 373 | case 'DIGEST-MD5': 374 | $this->send(""); 375 | break; 376 | } 377 | } else { 378 | $this->send(""); 379 | } 380 | } 381 | } 382 | 383 | /** 384 | * SASL success handler 385 | * 386 | * @param string $xml 387 | */ 388 | protected function sasl_success_handler($xml) { 389 | $this->log->log("Auth success!"); 390 | $this->authed = true; 391 | $this->reset(); 392 | } 393 | 394 | /** 395 | * SASL feature handler 396 | * 397 | * @param string $xml 398 | */ 399 | protected function sasl_failure_handler($xml) { 400 | $this->log->log("Auth failed!", XMPPHP_Log::LEVEL_ERROR); 401 | $this->disconnect(); 402 | 403 | throw new XMPPHP_Exception('Auth failed!'); 404 | } 405 | 406 | /** 407 | * Handle challenges for DIGEST-MD5 auth 408 | * 409 | * @param string $xml 410 | */ 411 | protected function sasl_challenge_handler($xml) { 412 | // Decode and parse the challenge string 413 | // (may be something like foo="bar",foo2="bar2,bar3,bar4",foo3=bar5 ) 414 | $challenge = base64_decode($xml->data); 415 | $vars = array(); 416 | $matches = array(); 417 | preg_match_all('/(\w+)=(?:"([^"]*)|([^,]*))/', $challenge, $matches); 418 | $res = array(); 419 | foreach ($matches[1] as $k => $v) { 420 | $vars[$v] = (empty($matches[2][$k]) ? $matches[3][$k] : $matches[2][$k]); 421 | } 422 | 423 | if (isset($vars['nonce'])) { 424 | // First step 425 | $vars['cnonce'] = uniqid(mt_rand(), false); 426 | $vars['nc'] = '00000001'; 427 | $vars['qop'] = 'auth'; // Force qop to auth 428 | if (!isset($vars['digest-uri'])) 429 | $vars['digest-uri'] = 'xmpp/' . $this->server; 430 | 431 | // now, the magic... 432 | $a1 = sprintf('%s:%s:%s', $this->user, $vars['realm'], $this->password); 433 | if ($vars['algorithm'] == 'md5-sess') { 434 | $a1 = pack('H32', md5($a1)) . ':' . $vars['nonce'] . ':' . $vars['cnonce']; 435 | } 436 | $a2 = "AUTHENTICATE:" . $vars['digest-uri']; 437 | $password = md5($a1) . ':' . $vars['nonce'] . ':' . $vars['nc'] . ':' . $vars['cnonce'] . ':' . $vars['qop'] . ':' . md5($a2); 438 | $password = md5($password); 439 | $response = sprintf('username="%s",realm="%s",nonce="%s",cnonce="%s",nc=%s,qop=%s,digest-uri="%s",response=%s,charset=utf-8', 440 | $this->user, $vars['realm'], $vars['nonce'], $vars['cnonce'], $vars['nc'], $vars['qop'], $vars['digest-uri'], $password); 441 | 442 | // Send the response 443 | $response = base64_encode($response); 444 | $this->send("$response"); 445 | } else { 446 | if (isset($vars['rspauth'])) { 447 | // Second step 448 | $this->send(""); 449 | } else { 450 | $this->log->log("ERROR receiving challenge : " . $challenge, XMPPHP_Log::LEVEL_ERROR); 451 | } 452 | } 453 | } 454 | 455 | /** 456 | * Resource bind handler 457 | * 458 | * @param string $xml 459 | */ 460 | protected function resource_bind_handler($xml) { 461 | if ($xml->attrs['type'] == 'result') { 462 | $this->log->log("Bound to " . $xml->sub('bind')->sub('jid')->data); 463 | $this->fulljid = $xml->sub('bind')->sub('jid')->data; 464 | $jidarray = explode('/', $this->fulljid); 465 | $this->jid = $jidarray[0]; 466 | } 467 | $id = $this->getId(); 468 | $this->addIdHandler($id, 'session_start_handler'); 469 | $this->send(""); 470 | } 471 | 472 | /** 473 | * Retrieves the roster 474 | * 475 | */ 476 | public function getRoster() { 477 | $id = $this->getID(); 478 | $this->send(""); 479 | } 480 | 481 | /** 482 | * Roster iq handler 483 | * Gets all packets matching XPath "iq/{jabber:iq:roster}query' 484 | * 485 | * @param string $xml 486 | */ 487 | protected function roster_iq_handler($xml) { 488 | $status = "result"; 489 | $xmlroster = $xml->sub('query'); 490 | foreach ($xmlroster->subs as $item) { 491 | $groups = array(); 492 | if ($item->name == 'item') { 493 | $jid = $item->attrs['jid']; //REQUIRED 494 | $name = $item->attrs['name']; //MAY 495 | $subscription = $item->attrs['subscription']; 496 | foreach ($item->subs as $subitem) { 497 | if ($subitem->name == 'group') { 498 | $groups[] = $subitem->data; 499 | } 500 | } 501 | $contacts[] = array($jid, $subscription, $name, $groups); //Store for action if no errors happen 502 | } else { 503 | $status = "error"; 504 | } 505 | } 506 | if ($status == "result") { //No errors, add contacts 507 | foreach ($contacts as $contact) { 508 | $this->roster->addContact($contact[0], $contact[1], $contact[2], $contact[3]); 509 | } 510 | } 511 | if ($xml->attrs['type'] == 'set') { 512 | $this->send("attrs['id']}\" to=\"{$xml->attrs['from']}\" />"); 513 | } 514 | } 515 | 516 | /** 517 | * Session start handler 518 | * 519 | * @param string $xml 520 | */ 521 | protected function session_start_handler($xml) { 522 | $this->log->log("Session started"); 523 | $this->session_started = true; 524 | $this->event('session_start'); 525 | } 526 | 527 | /** 528 | * TLS proceed handler 529 | * 530 | * @param string $xml 531 | */ 532 | protected function tls_proceed_handler($xml) { 533 | $this->log->log("Starting TLS encryption"); 534 | stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT); 535 | $this->reset(); 536 | } 537 | 538 | /** 539 | * Retrieves the vcard 540 | * 541 | */ 542 | public function getVCard($jid = Null) { 543 | $id = $this->getID(); 544 | $this->addIdHandler($id, 'vcard_get_handler'); 545 | if ($jid) { 546 | $this->send(""); 547 | } else { 548 | $this->send(""); 549 | } 550 | } 551 | 552 | /** 553 | * VCard retrieval handler 554 | * 555 | * @param XML Object $xml 556 | */ 557 | protected function vcard_get_handler($xml) { 558 | $vcard_array = array(); 559 | $vcard = $xml->sub('vcard'); 560 | // go through all of the sub elements and add them to the vcard array 561 | foreach ($vcard->subs as $sub) { 562 | if ($sub->subs) { 563 | $vcard_array[$sub->name] = array(); 564 | foreach ($sub->subs as $sub_child) { 565 | $vcard_array[$sub->name][$sub_child->name] = $sub_child->data; 566 | } 567 | } else { 568 | $vcard_array[$sub->name] = $sub->data; 569 | } 570 | } 571 | $vcard_array['from'] = $xml->attrs['from']; 572 | $this->event('vcard', $vcard_array); 573 | } 574 | 575 | /** 576 | * Checking if the given username exists and the given text password 577 | * is correct by trying to connect to and authenticate on xmpp server. 578 | * @author del Rosario Ernesto 579 | * @return bool 580 | */ 581 | public static function check_username_and_password($user = "", $password = "", $conf = array()) { 582 | if (empty($user) || empty($password) || !isset($conf["host"]) || !isset($conf["port"])) 583 | return null; 584 | 585 | $xmpp = new XMPPHP_XMPP( 586 | $conf["host"], 587 | $conf["port"], 588 | $user, 589 | $password, 590 | $conf["resource"], 591 | $conf["server"], 592 | $conf["printlog"], 593 | $conf["loglevel"] 594 | ); 595 | 596 | try { 597 | $xmpp->connect(); 598 | $xmpp->processUntil('session_start'); 599 | $xmpp->disconnect(); 600 | return true; 601 | } catch(XMPPHP_Exception $e) { 602 | return false; 603 | } 604 | } 605 | 606 | } 607 | -------------------------------------------------------------------------------- /XMPPHP/XMPP_Old.php: -------------------------------------------------------------------------------- 1 | 25 | * @author Stephan Wentz 26 | * @author Michael Garvin 27 | * @copyright 2008 Nathanael C. Fritz 28 | */ 29 | /** XMPPHP_XMPP 30 | * 31 | * This file is unnecessary unless you need to connect to older, non-XMPP-compliant servers like Dreamhost's. 32 | * In this case, use instead of XMPPHP_XMPP, otherwise feel free to delete it. 33 | * The old Jabber protocol wasn't standardized, so use at your own risk. 34 | * 35 | */ 36 | require_once "XMPP.php"; 37 | 38 | class XMPPHP_XMPPOld extends XMPPHP_XMPP { 39 | 40 | /** 41 | * 42 | * @var string 43 | */ 44 | protected $session_id; 45 | 46 | public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null) { 47 | parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel); 48 | if (!$server) 49 | $server = $host; 50 | $this->stream_start = ''; 51 | $this->fulljid = "{$user}@{$server}/{$resource}"; 52 | } 53 | 54 | /** 55 | * Override XMLStream's startXML 56 | * 57 | * @param parser $parser 58 | * @param string $name 59 | * @param array $attr 60 | */ 61 | public function startXML($parser, $name, $attr) { 62 | if ($this->xml_depth == 0) { 63 | $this->session_id = $attr['ID']; 64 | $this->authenticate(); 65 | } 66 | parent::startXML($parser, $name, $attr); 67 | } 68 | 69 | /** 70 | * Send Authenticate Info Request 71 | * 72 | */ 73 | public function authenticate() { 74 | $id = $this->getId(); 75 | $this->addidhandler($id, 'authfieldshandler'); 76 | $this->send("{$this->user}"); 77 | } 78 | 79 | /** 80 | * Retrieve auth fields and send auth attempt 81 | * 82 | * @param XMLObj $xml 83 | */ 84 | public function authFieldsHandler($xml) { 85 | $id = $this->getId(); 86 | $this->addidhandler($id, 'oldAuthResultHandler'); 87 | if ($xml->sub('query')->hasSub('digest')) { 88 | $hash = sha1($this->session_id . $this->password); 89 | print "{$this->session_id} {$this->password}\n"; 90 | $out = "{$this->user}{$hash}{$this->resource}"; 91 | } else { 92 | $out = "{$this->user}{$this->password}{$this->resource}"; 93 | } 94 | $this->send($out); 95 | } 96 | 97 | /** 98 | * Determine authenticated or failure 99 | * 100 | * @param XMLObj $xml 101 | */ 102 | public function oldAuthResultHandler($xml) { 103 | if ($xml->attrs['type'] != 'result') { 104 | $this->log->log("Auth failed!", XMPPHP_Log::LEVEL_ERROR); 105 | $this->disconnect(); 106 | throw new XMPPHP_Exception('Auth failed!'); 107 | } else { 108 | $this->log->log("Session started"); 109 | $this->event('session_start'); 110 | } 111 | } 112 | 113 | } 114 | 115 | ?> 116 | -------------------------------------------------------------------------------- /cli_longrun_example.php: -------------------------------------------------------------------------------- 1 | autoSubscribe(); 12 | 13 | $vcard_request = array(); 14 | 15 | try { 16 | $conn->connect(); 17 | while (!$conn->isDisconnected()) { 18 | $payloads = $conn->processUntil(array('message', 'presence', 'end_stream', 'session_start', 'vcard')); 19 | foreach ($payloads as $event) { 20 | $pl = $event[1]; 21 | switch ($event[0]) { 22 | case 'message': 23 | print "---------------------------------------------------------------------------------\n"; 24 | print "Message from: {$pl['from']}\n"; 25 | if ($pl['subject']) 26 | print "Subject: {$pl['subject']}\n"; 27 | print $pl['body'] . "\n"; 28 | print "---------------------------------------------------------------------------------\n"; 29 | $conn->message($pl['from'], $body = "Thanks for sending me \"{$pl['body']}\".", $type = $pl['type']); 30 | $cmd = explode(' ', $pl['body']); 31 | if ($cmd[0] == 'quit') 32 | $conn->disconnect(); 33 | if ($cmd[0] == 'break') 34 | $conn->send(""); 35 | if ($cmd[0] == 'vcard') { 36 | if (!($cmd[1])) 37 | $cmd[1] = $conn->user . '@' . $conn->server; 38 | // take a note which user requested which vcard 39 | $vcard_request[$pl['from']] = $cmd[1]; 40 | // request the vcard 41 | $conn->getVCard($cmd[1]); 42 | } 43 | break; 44 | case 'presence': 45 | print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n"; 46 | break; 47 | case 'session_start': 48 | print "Session Start\n"; 49 | $conn->getRoster(); 50 | $conn->presence($status = "Cheese!"); 51 | break; 52 | case 'vcard': 53 | // check to see who requested this vcard 54 | $deliver = array_keys($vcard_request, $pl['from']); 55 | // work through the array to generate a message 56 | print_r($pl); 57 | $msg = ''; 58 | foreach ($pl as $key => $item) { 59 | $msg .= "$key: "; 60 | if (is_array($item)) { 61 | $msg .= "\n"; 62 | foreach ($item as $subkey => $subitem) { 63 | $msg .= " $subkey: $subitem\n"; 64 | } 65 | } else { 66 | $msg .= "$item\n"; 67 | } 68 | } 69 | // deliver the vcard msg to everyone that requested that vcard 70 | foreach ($deliver as $sendjid) { 71 | // remove the note on requests as we send out the message 72 | unset($vcard_request[$sendjid]); 73 | $conn->message($sendjid, $msg, 'chat'); 74 | } 75 | break; 76 | } 77 | } 78 | } 79 | } catch (XMPPHP_Exception $e) { 80 | die($e->getMessage()); 81 | } 82 | -------------------------------------------------------------------------------- /cli_longrun_example_bosh.php: -------------------------------------------------------------------------------- 1 | autoSubscribe(); 12 | 13 | try { 14 | $conn->connect('http://server.tld:5280/xmpp-httpbind'); 15 | while (!$conn->isDisconnected()) { 16 | $payloads = $conn->processUntil(array('message', 'presence', 'end_stream', 'session_start')); 17 | foreach ($payloads as $event) { 18 | $pl = $event[1]; 19 | switch ($event[0]) { 20 | case 'message': 21 | print "---------------------------------------------------------------------------------\n"; 22 | print "Message from: {$pl['from']}\n"; 23 | if ($pl['subject']) 24 | print "Subject: {$pl['subject']}\n"; 25 | print $pl['body'] . "\n"; 26 | print "---------------------------------------------------------------------------------\n"; 27 | $conn->message($pl['from'], $body = "Thanks for sending me \"{$pl['body']}\".", $type = $pl['type']); 28 | if ($pl['body'] == 'quit') 29 | $conn->disconnect(); 30 | if ($pl['body'] == 'break') 31 | $conn->send(""); 32 | break; 33 | case 'presence': 34 | print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n"; 35 | break; 36 | case 'session_start': 37 | print "Session Start\n"; 38 | $conn->getRoster(); 39 | $conn->presence($status = "Cheese!"); 40 | break; 41 | } 42 | } 43 | } 44 | } catch (XMPPHP_Exception $e) { 45 | die($e->getMessage()); 46 | } 47 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heidisoft/xmpphp", 3 | "description": "XMPPHP is an elegant PHP library for XMPP (Jabber, Google Talk, Facebook etc).", 4 | "homepage": "https://github.com/heidisoft/XMPPHP", 5 | "license": "GPL-2.0", 6 | "authors": [ 7 | { 8 | "name": "Heshan Wanigasooriya", 9 | "email": "heshan@heidisoft.com" 10 | } 11 | ], 12 | "minimum-stability": "dev", 13 | "require": { 14 | "php": ">=5.2" 15 | }, 16 | "extra": { 17 | "branch-alias": { 18 | "dev-master": "1.0.x-dev" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sendmessage_example.php: -------------------------------------------------------------------------------- 1 | connect(); 14 | $conn->processUntil('session_start'); 15 | $conn->presence(); 16 | $conn->message('someguy@someserver.net', 'This is a test message!'); 17 | $conn->disconnect(); 18 | } catch (XMPPHP_Exception $e) { 19 | die($e->getMessage()); 20 | } 21 | -------------------------------------------------------------------------------- /tests/AllTests.php: -------------------------------------------------------------------------------- 1 | addTestSuite('XMPPHP_LogTest'); 22 | $suite->addTestSuite('XMPPHP_XMLObjTest'); 23 | $suite->addTestSuite('XMPPHP_XMPPTest'); 24 | 25 | return $suite; 26 | } 27 | } 28 | 29 | if (PHPUnit_MAIN_METHOD == 'AllTests::main') { 30 | AllTests::main(); 31 | } 32 | -------------------------------------------------------------------------------- /tests/XMPPHP/LogTest.php: -------------------------------------------------------------------------------- 1 | log('test'); 15 | $result = ob_get_clean(); 16 | 17 | $this->assertEquals('', $result); 18 | } 19 | 20 | public function testPrintoutOutput() 21 | { 22 | $log = new XMPPHP_Log(true); 23 | 24 | $msg = 'I am a test log message'; 25 | 26 | ob_start(); 27 | $log->log($msg); 28 | $result = ob_get_clean(); 29 | 30 | $this->assertContains($msg, $result); 31 | } 32 | 33 | public function testPrintoutNoOutputWithDefaultLevel() 34 | { 35 | $log = new XMPPHP_Log(true, XMPPHP_Log::LEVEL_ERROR); 36 | 37 | $msg = 'I am a test log message'; 38 | 39 | ob_start(); 40 | $log->log($msg); 41 | $result = ob_get_clean(); 42 | 43 | $this->assertSame('', $result); 44 | } 45 | 46 | public function testPrintoutOutputWithDefaultLevel() 47 | { 48 | $log = new XMPPHP_Log(true, XMPPHP_Log::LEVEL_INFO); 49 | 50 | $msg = 'I am a test log message'; 51 | 52 | ob_start(); 53 | $log->log($msg); 54 | $result = ob_get_clean(); 55 | 56 | $this->assertContains($msg, $result); 57 | } 58 | 59 | public function testPrintoutNoOutputWithCustomLevel() 60 | { 61 | $log = new XMPPHP_Log(true, XMPPHP_Log::LEVEL_INFO); 62 | 63 | $msg = 'I am a test log message'; 64 | 65 | ob_start(); 66 | $log->log($msg, XMPPHP_Log::LEVEL_DEBUG); 67 | $result = ob_get_clean(); 68 | 69 | $this->assertSame('', $result); 70 | } 71 | 72 | public function testPrintoutOutputWithCustomLevel() 73 | { 74 | $log = new XMPPHP_Log(true, XMPPHP_Log::LEVEL_INFO); 75 | 76 | $msg = 'I am a test log message'; 77 | 78 | ob_start(); 79 | $log->log($msg, XMPPHP_Log::LEVEL_INFO); 80 | $result = ob_get_clean(); 81 | 82 | $this->assertContains($msg, $result); 83 | } 84 | 85 | public function testExplicitPrintout() 86 | { 87 | $log = new XMPPHP_Log(false); 88 | 89 | $msg = 'I am a test log message'; 90 | 91 | ob_start(); 92 | $log->log($msg); 93 | $result = ob_get_clean(); 94 | 95 | $this->assertSame('', $result); 96 | } 97 | 98 | public function testExplicitPrintoutResult() 99 | { 100 | $log = new XMPPHP_Log(false); 101 | 102 | $msg = 'I am a test log message'; 103 | 104 | ob_start(); 105 | $log->log($msg); 106 | $result = ob_get_clean(); 107 | 108 | $this->assertSame('', $result); 109 | 110 | ob_start(); 111 | $log->printout(); 112 | $result = ob_get_clean(); 113 | 114 | $this->assertContains($msg, $result); 115 | } 116 | 117 | public function testExplicitPrintoutClear() 118 | { 119 | $log = new XMPPHP_Log(false); 120 | 121 | $msg = 'I am a test log message'; 122 | 123 | ob_start(); 124 | $log->log($msg); 125 | $result = ob_get_clean(); 126 | 127 | $this->assertSame('', $result); 128 | 129 | ob_start(); 130 | $log->printout(); 131 | $result = ob_get_clean(); 132 | 133 | $this->assertContains($msg, $result); 134 | 135 | ob_start(); 136 | $log->printout(); 137 | $result = ob_get_clean(); 138 | 139 | $this->assertSame('', $result); 140 | } 141 | 142 | public function testExplicitPrintoutLevel() 143 | { 144 | $log = new XMPPHP_Log(false, XMPPHP_Log::LEVEL_ERROR); 145 | 146 | $msg = 'I am a test log message'; 147 | 148 | ob_start(); 149 | $log->log($msg); 150 | $result = ob_get_clean(); 151 | 152 | $this->assertSame('', $result); 153 | 154 | ob_start(); 155 | $log->printout(true, XMPPHP_Log::LEVEL_INFO); 156 | $result = ob_get_clean(); 157 | 158 | $this->assertSame('', $result); 159 | } 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /tests/XMPPHP/XMLObjTest.php: -------------------------------------------------------------------------------- 1 | "; 12 | 13 | $result = $xmlobj->toString(); 14 | 15 | $this->assertSame($expected, $result); 16 | } 17 | 18 | public function testToStringNameNamespaceAttr() 19 | { 20 | $xmlobj = new XMPPHP_XMLObj('testName', 'testNameSpace', array('attr1'=>'valA', 'attr2'=>'valB')); 21 | 22 | $expected = ""; 23 | 24 | $result = $xmlobj->toString(); 25 | 26 | $this->assertSame($expected, $result); 27 | } 28 | 29 | public function testToStringNameNamespaceData() 30 | { 31 | $xmlobj = new XMPPHP_XMLObj('testName', 'testNameSpace', array(), 'I am test data'); 32 | 33 | $expected = "I am test data"; 34 | 35 | $result = $xmlobj->toString(); 36 | 37 | $this->assertSame($expected, $result); 38 | } 39 | 40 | public function testToStringNameNamespaceSub() 41 | { 42 | $xmlobj = new XMPPHP_XMLObj('testName', 'testNameSpace'); 43 | $sub1 = new XMPPHP_XMLObj('subName', 'subNameSpace'); 44 | $xmlobj->subs = array($sub1); 45 | 46 | $expected = ""; 47 | 48 | $result = $xmlobj->toString(); 49 | 50 | $this->assertSame($expected, $result); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /tests/XMPPHP/XMPPTest.php: -------------------------------------------------------------------------------- 1 | useEncryption(false); 12 | $xmpp->connect(10); 13 | $xmpp->processUntil('session_start'); 14 | $xmpp->presence(); 15 | $xmpp->message('stephan@jabber.wentz.it', 'This is a test message!'); 16 | $xmpp->disconnect(); 17 | } catch(XMPPHP_Exception $e) { 18 | return; 19 | } catch(Exception $e) { 20 | $this->fail('Unexpected Exception thrown: '.$e->getMessage()); 21 | } 22 | 23 | $this->fail('Expected XMPPHP_Exception not thrown!'); 24 | } 25 | 26 | public function testAuthException() 27 | { 28 | try { 29 | $xmpp = new XMPPHP_XMPP('jabber.wentz.it', 5222, 'invalidusername', 'invalidpassword', 'xmpphp', 'jabber.wentz.it', true, XMPPHP_Log::LEVEL_VERBOSE); 30 | $xmpp->useEncryption(false); 31 | $xmpp->connect(10); 32 | $xmpp->processUntil('session_start'); 33 | $xmpp->presence(); 34 | $xmpp->message('stephan@jabber.wentz.it', 'This is a test message!'); 35 | $xmpp->disconnect(); 36 | } catch(XMPPHP_Exception $e) { 37 | return; 38 | } catch(Exception $e) { 39 | $this->fail('Unexpected Exception thrown: '.$e->getMessage()); 40 | } 41 | 42 | $this->fail('Expected XMPPHP_Exception not thrown!'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /webclient_example.php: -------------------------------------------------------------------------------- 1 | "; 10 | 11 | #Use XMPPHP_Log::LEVEL_VERBOSE to get more logging for error reports 12 | #If this doesn't work, are you running 64-bit PHP with < 5.2.6? 13 | $conn = new XMPPHP_BOSH('server.tld', 5280, 'user', 'password', 'xmpphp', 'server.tld', $printlog = true, $loglevel = XMPPHP_Log::LEVEL_INFO); 14 | $conn->autoSubscribe(); 15 | 16 | try { 17 | if (isset($_SESSION['messages'])) { 18 | foreach ($_SESSION['messages'] as $msg) { 19 | print $msg; 20 | flush(); 21 | } 22 | } 23 | $conn->connect('http://server.tld:5280/xmpp-httpbind', 1, true); 24 | #while(true) { 25 | $payloads = $conn->processUntil(array('message', 'presence', 'end_stream', 'session_start')); 26 | foreach ($payloads as $event) { 27 | $pl = $event[1]; 28 | switch ($event[0]) { 29 | case 'message': 30 | if (!isset($_SESSION['messages'])) 31 | $_SESSION['message'] = Array(); 32 | $msg = "---------------------------------------------------------------------------------\n{$pl['from']}: {$pl['body']}\n"; 33 | print $msg; 34 | $_SESSION['messages'][] = $msg; 35 | flush(); 36 | $conn->message($pl['from'], $body = "Thanks for sending me \"{$pl['body']}\".", $type = $pl['type']); 37 | if ($pl['body'] == 'quit') 38 | $conn->disconnect(); 39 | if ($pl['body'] == 'break') 40 | $conn->send(""); 41 | break; 42 | case 'presence': 43 | print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n"; 44 | break; 45 | case 'session_start': 46 | print "Session Start\n"; 47 | $conn->getRoster(); 48 | $conn->presence($status = "Cheese!"); 49 | break; 50 | } 51 | } 52 | #} 53 | } catch (XMPPHP_Exception $e) { 54 | die($e->getMessage()); 55 | } 56 | $conn->saveSession(); 57 | 58 | print ""; 59 | print ""; 60 | ?> 61 | --------------------------------------------------------------------------------