source: contrib/davical/inc/pubsub.php @ 3733

Revision 3733, 25.7 KB checked in by gabriel.malheiros, 13 years ago (diff)

Ticket #1541 - <Davical customizado para o Expresso.Utiliza Caldav e CardDav?>

Line 
1<?
2
3/**********************************************************************
4 *                       XMPP PubSub for DAViCal
5 *           Copyright 2009 Rob Ostensen rob@boxacle.net
6 *         Licenced http://gnu.org/copyleft/gpl.html GNU GPL v2
7 *
8 *********************************************************************/
9
10
11class xmpp
12{
13        private $connection,$streamTagBegin,$streamTagEnd,$mesgcount=0,$ready,$moredata=false,$username,$stream,$xmlparser,$xquery;
14        private $namespaces = Array();
15        private $recvTags = Array();
16        private $recvHandlers = Array();
17        private $sendHandlers = Array();
18        private $finishedCommands = Array();
19        private $sendQueue = Array();
20        private $recvQueue = '';
21        private $pubsubNext = Array();
22        private $depth = 0,$processDepth=0;
23        public $server,$port,$jid,$resource,$password,$tls,$idle,$status,$pubsubLayout='hometree';
24
25        // constructor
26        public function __construct ( )
27        {
28                $this->status = "online";
29                $this->setupXmlParser ();
30        }
31
32        // figure out what server to connect to and make the connection, returns true if successful, false otherwise
33        private function connect ()
34        {
35                if ( ! isset ( $this->jid ) )
36                        return $this->connection = false;
37                if ( ! isset ( $this->idle ) )
38                        $this->idle = true;
39                if ( ! isset ( $this->resource ) )
40                        $this->resource = 'caldav' . getmypid();
41                if ( ! preg_match ( '/^\//', $this->resource ) )
42                        $this->resource = '/' . $this->resource;
43                $temp = explode ( '@', $this->jid );
44                $this->username = $temp[0];
45                if ( ! isset ( $this->server ) )
46                {
47                        $this->server = $temp[1];
48                }
49                $r = dns_get_record("_xmpp-client._tcp.". $this->server , DNS_SRV);
50                if ( 0 < count ( $r ) )
51                {
52                        $this->original_server   = $this->server;
53                        $this->server            = $r[0]['target'];
54                        $this->original_port     = $this->port;
55                        $this->port              = $r[0]['port'];
56                }
57                if ( ! isset ( $this->port ) )
58                        $this->port = 5222;
59                if ( 'ssl' == $this->tls || ( ! isset ( $this->tls ) && 5223 == $this->port ) )
60                        $url = 'ssl://' . $this->server;
61                elseif ( 'tls' == $this->tls || ( ! isset ( $this->tls ) && 5222 == $this->port ) )
62                        $url = 'tcp://' . $this->server;
63                else
64                        $url = 'tcp://' . $this->server;
65                if ( isset ( $this->original_server ) )
66                        $this->server = $this->original_server;
67                $this->connection = stream_socket_client ( $url . ':' . $this->port, $errno, $errstring, 10, STREAM_CLIENT_ASYNC_CONNECT );
68                if ( false === $this->connection )
69                {
70                        if ( $errno != 0 )
71                                $log = $errstring;
72                        return false;
73                }
74                $this->initializeQueue ( );
75                socket_set_blocking ( $this->connection, false );
76                return true;
77        }
78
79        // handles the features tag, mostly related to authentication
80        private function handleFeatures ( &$node )
81        {
82                if ( $this->debug ) $this->log ( 'handling features' );
83                if ( 'STARTTLS' == $node->firstChild->nodeName )
84                {
85                        $this->sendQueue[] = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
86                        return;
87                }
88                $elements = $this->query ( '*/MECHANISM', $node );
89                if ( ! is_null ( $elements )  && $elements !== false )
90                {
91                        if ( $this->debug ) $this->log ( " found " . $elements->length . " matching MECHANISM nodes ");
92                        $auth_mech = array ();
93                        foreach ( $elements as $e )
94                                $auth_mech[] = $e->nodeValue;
95                        if ( in_array ( 'PLAIN', $auth_mech ) )
96                                $this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . preg_replace('/@.*$/','',$this->jid) . "\x00" . $this->password) . "</auth>";
97                        elseif ( in_array ( 'DIGEST-MD5', $auth_mech ) ) // this code and the associated function are UNTESTED
98                        {
99                                $this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>";
100                                $this->recvHandlers['challenge'] = 'digestAuth' ;
101                        }
102                        $this->recvHandlers['success'] = 'handleSuccess' ;
103                }
104                $elements = $this->query ( '*/BIND', $node );
105                if ( ! is_null ( $elements ) && $elements->length > 0 )
106                {
107                        // failure if we don't hit this, not sure how we can detect that failure yet.
108                        if ( $this->debug ) $this->log ( " found " . $elements->length . " matching BIND nodes ");
109                        $this->ready = true;
110                }
111        }
112
113        // handle proceed tag/enable tls
114        private function enableTLS ( $node )
115        {
116                stream_set_blocking ( $this->connection, true );
117                stream_socket_enable_crypto ( $this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
118                stream_set_blocking ( $this->connection, false );
119                $this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
120        }
121
122        // do digest auth
123        private function digestAuth ( &$node )
124        {
125                // this code is based solely on the description found @ http://web.archive.org/web/20050224191820/http://cataclysm.cx/wip/digest-md5-crash.html
126                // UNTESTED please shoot me an email if you get this to work !!
127                $contents = $node->nodeValue;
128                if ( ! is_null ( $elements ) )
129                {
130                        $challlenge = array ();
131                        $parts = explode ( ',', base64_decode ( $contents ) );
132                        foreach ( $parts as $text )
133                        {
134                                $temp = explode ( '=', $text );
135                                $challenge[$temp[0]] = $temp[1];
136                        }
137                        if ( $challenge['realm'] == $this->server ) // might fail need to handle a response with multiple realms
138                        {
139                                $cnonce =  md5((mt_rand() * time() / mt_rand())+$challenge['nonce']);
140                                $X =  md5 ( preg_replace('/@.*$/','',$this->jid) . ':' . $this->server . ':' .  $this->password, true );
141                                $HA1 = md5 ( $X . ':' . $challenge['nonce'] . ':' . $cnonce . ':' . $this->jid . $this->resource );
142                                $HA2 = md5 ( "AUTHENTICATE:xmpp/" . $this->server );
143                                $resp = md5 ( $HA1 . ':' . $challenge['nonce'] . ':00000001:' . $cnonce . ':auth' . $HA2 );
144                                $this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" .
145                                        base64_encode("username=\"" . preg_replace('/@.*$/','',$this->jid) . "\"," .
146                                        "realm=\"" . $this->server . "\",nonce=\"" . $challenge['nonce'] . "\",cnonce=\"". $cnonce . "\"," .
147                                        "nc=00000001,qop=auth,digest-uri=\"xmpp/" . $this->server . "\",response=" . $resp .
148                                        ",charset=utf-8,authzid=\"". $this->jid . $this->resource . "\"" ) . "</response>" // note the PID component to the resource, just incase
149                                        ;
150                        }
151                        elseif ( $challenge['rspauth'] )
152                                $this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>" ;
153                }
154        }
155
156        // do basic setup to get the connection logged in and going
157        private function handleSuccess ( &$node )
158        {
159                $this->loggedIn = true;
160                $this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
161                $this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>" . preg_replace('/^\//','',$this->resource) . "</resource></bind></iq>";
162                $this->recvHandlers['stream:error'] = 'handleError' ;
163                $this->recvHandlers['iq'] = 'handleIq' ;
164                $this->recvHandlers['message'] = 'handleMessage' ;
165                $this->mesgcount = 1;
166        }
167
168        // do something with standard iq messages also does some standard setup like setting presence
169        private function handleIq ( &$node )
170        {
171                if ( $this->debug ) $this->log ( "Handle IQ id:" . $node->getAttribute ( 'id' ) . ' type:' . $node->getAttribute ( 'type' ) . "");
172                if ( $node->getAttribute ( 'type' ) == 'result' || $node->getAttribute ( 'type' ) == 'error' )
173                {
174                        $commandId = $node->getAttribute ( 'id' );
175                        $this->command[$commandId] = true;
176                        if ( isset ( $this->handleCommand[$commandId] ) )
177                        {
178                                $this->finishedCommands[$commandId] = true;
179                                if ( method_exists ( $this, $this->handleCommand[$commandId] ) )
180                                        call_user_func_array ( array ( $this, $this->handleCommand[$commandId] ),  array ( &$node ) );
181                                else
182                                        call_user_func_array ( $this->handleCommand[$commandId], array ( &$node ) );
183                        }
184                }
185                if ( $node->getAttribute ( 'id' ) == $this->mesgcount && $this->mesgcount < 3 )
186                {
187                        $this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='" . ( $this->mesgcount++ ) . "'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>";
188                        $this->sendQueue[] = "<iq xmlns='jabber:client' type='get' id='" . ( $this->mesgcount++ ) . "'><query xmlns='jabber:iq:roster' /></iq>";
189                }
190                if ( $node->getAttribute ( 'id' ) == '2' && $this->command['2'] == true )
191                {
192                        $this->nextreply = $this->mesgcount++;
193                        $this->sendQueue[] = "<presence id='" . $this->nextreply . "' ><status>" . $this->status . '</status></presence>';
194                        $this->ready = true;
195                }
196        }
197
198        // do something with standard messages
199        private function handleMessage ( &$node )
200        {
201                if ( $node->getAttribute ( 'type' ) == 'chat' )
202                {
203                        $this->command[$node->getAttribute ( 'id' )] = true;
204                        $elements = $this->query ( '//*/body', $node );
205                        if ( 0 < $elements->length )
206                        {
207                                $temp = $elements->items(0);
208                                if ( $this->debug ) $this->log ( "received message " . $temp->nodeValue );
209                        }
210                }
211        }
212
213        // handle stream errors by logging a message and closing the connection
214        private function handleError ( &$node )
215        {
216                $this->log ( 'STREAM ERROR OCCURRED! XMPP closing connection, this is probably a bug' );
217                $this->idle = false;
218                $this->close ();
219        }
220
221        //  disco a pubsub collection
222        private function disco ( $to, $type, $name )
223        {
224                $msg = $this->mesgcount++;
225                $send  = "<iq type='get' from='" . $this->jid . $this->resource . "' to='$to' id='" . $msg . "'>";
226                $send .= "      <query xmlns='http://jabber.org/protocol/disco#$type' node='$name'/>";
227                $send .= "</iq>";
228                $this->handleCommand[$msg] = 'discoResult';
229                $this->sendQueue[] = $send;
230                $this->go();
231        }
232
233        //  result from disco
234        private function discoResult ( &$node )
235        {
236                if ( $this->debug ) $this->log ( $node->ownerDocument->saveXML($node) );
237                $id = $node->getAttribute ( 'id' );
238                $identity = $this->query ( '*/IDENTITY', $node );
239                if ( @is_array ( $this->pubsub [ 'create' ] [ $id ] ) && 0 == $identity->length )
240                {
241                        $this->pubsubCreateNode( $this->pubsub [ 'create' ] [ $id ] [ 0 ],
242                                $this->pubsub [ 'create' ] [ $id ] [ 1 ],
243                                $this->pubsub [ 'create' ] [ $id ] [ 2 ],
244                                $this->pubsub [ 'create' ] [ $id ] [ 3 ] );
245                }
246        }
247
248        //  send a message to a jid
249        public function sendMessage ( $to, $message )
250        {
251                $msg = $this->mesgcount++;
252                $out .= "<message id='" . $msg . "' from='" . $this->jid . $this->resource . "' to='" . $to. "' >";
253                $out .= "<body>" . $message . "</body></message>";
254                $this->sendQueue[] = $out;
255                $this->go();
256        }
257
258        //      get a pubsub collection/leaf node and create if it doesn't exist
259        public function pubsubCreate ( $to, $type, $name, $configure = null )
260        {
261                if ( 1 > strlen ( $to ) )
262                        $to = 'pubsub.' . $this->server;
263                if ( 1 > strlen ( $type ) )
264                        $type = 'set';
265                if ( 'hometree' == $this->pubsubLayout )
266                        $node = '/home/' . $this->server . '/' . $this->username . $name;
267                else
268                        $node= $name;
269                $this->pubsub['create'][$this->mesgcount+1] = array ( $to, $type, $name, $configure );
270                $this->disco ( $to, 'info', $node );
271        }
272
273        //      create a pubsub collection/leaf node
274        private function pubsubCreateNode ( $to, $type, $name, $configure = null )
275        {
276                if ( 'hometree' == $this->pubsubLayout )
277                        $node = '/home/' . $this->server . '/' . $this->username . $name;
278                else
279                        $node= $name;
280                $msg = $this->mesgcount++;
281                $out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
282                $out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub" ><create node="' . $node . '"/>';
283                if ( $configure )
284                        $out .= '<configure>' . $configure .' </configure>';
285                else
286                        $out .= '<configure/>';
287                $out .= '</pubsub>';
288                $out .= '</iq>';
289                $this->sendQueue[] = $out;
290                $this->handleCommand[ $msg ] = 'pubsubResult';
291                $this->go();
292        }
293
294        //      configure a pubsub collection or leaf
295        public function pubsubConfig ( $to, $type, $name )
296        {
297                if ( 'hometree' == $this->pubsubLayout )
298                        $node = '/home/' . $this->server . '/' . $this->username . $name;
299                else
300                        $node= $name;
301                $msg = $this->mesgcount++;
302                $out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="get" id="' . $msg . '">';
303                $out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub#owner" ><configure node="' . $node . '"/>';
304                $out .= '</pubsub>';
305                $out .= '</iq>';
306                $this->handleCommand[ $msg ] = 'pubsubResult';
307                $this->sendQueue[] = $out;
308                $this->go();
309        }
310
311        //      delete a pubsub collection or leaf
312        public function pubsubDelete ( $to, $type, $name )
313        {
314                if ( 'hometree' == $this->pubsubLayout )
315                        $node = '/home/' . $this->server . '/' . $this->username . $name;
316                else
317                        $node= $name;
318                $msg = $this->mesgcount++;
319                $out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
320                $out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"><delete node="' . $node . '"/>';
321                $out .= '</pubsub>';
322                $out .= '</iq>';
323                $this->handleCommand[ $msg ] = 'pubsubResult';
324                $this->sendQueue[] = $out;
325                $this->go();
326        }
327
328        //      purge a pubsub collection or leaf
329        public function pubsubPurge ( $to, $type, $name )
330        {
331                if ( 'hometree' == $this->pubsubLayout )
332                        $node = '/home/' . $this->server . '/' . $this->username . $name;
333                else
334                        $node= $name;
335                $msg = $this->mesgcount++;
336                $out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
337                $out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"><purge node="' . $node . '"/>';
338                $out .= '</pubsub>';
339                $out .= '</iq>';
340                $this->handleCommand[ $msg ] = 'pubsubResult';
341                $this->sendQueue[] = $out;
342                $this->go();
343        }
344
345        //      publish to a pubsub collection
346        public function pubsubPublish ( $to, $type, $name, $contents, $nodeId )
347        {
348                if ( 1 > strlen ( $to ) )
349                        $to = 'pubsub.' . $this->server;
350                if ( 1 > strlen ( $type ) )
351                        $type = 'set';
352                if ( 1 > strlen ( $nodeId ) )
353                        $id = "id='$nodeId'";
354                else
355                        $id = '';
356                if ( 'hometree' == $this->pubsubLayout )
357                        $node = '/home/' . $this->server . '/' . $this->username . $name;
358                else
359                        $node= $name;
360                $msg = $this->mesgcount++;
361                $out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
362                $out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="' . $node . '">';
363                if ( preg_match ( '/^<item/', $contents ) )
364                        $out .= $contents;
365                else
366                        $out .= '<item ' . $id . '>' . $contents . '</item>';
367                $out .= '</publish></pubsub>';
368                $out .= '</iq>';
369                $this->sendQueue[] = $out;
370                $this->handleCommand[ $msg ] = 'pubsubResult';
371                $this->go();
372        }
373
374        // subscribe to a pubsub collection,leaf or item
375        private function pubsubSubscribe ( $to, $type, $name )
376        {
377                $msg = $this->mesgcount++;
378                if ( 'hometree' == $this->pubsubLayout )
379                        $node = '/home/' . $this->server . '/' . $this->username . $name;
380                else
381                        $node= $name;
382                $out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
383                $out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub"><subscribe node="' . $name . '" jid="' . $this->jid . $this->resource . '"/>';
384                $out .= '</pubsub>';
385                $out .= '</iq>';
386                $this->sendQueue[] = $out;
387                $this->handleCommand[ $msg ] = 'pubsubResult';
388                $this->go();
389        }
390
391        private function pubsubResult ( &$node )
392        {
393                if ( $this->debug ) $this->log ( "pubsub RESULT   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
394                if ( $node->getAttribute ( 'type' ) == 'error' )
395                {
396                        $errnode = $this->query ( 'ERROR', $node );
397                        if ( $errnode->length > 0 && (  '403' == $errnode->item( 0 )->getAttribute ( 'code' ) || '500' == $errnode->item( 0 )->getAttribute ( 'code' ) ) )
398                        {
399                                if ( 'CREATE' == $node->firstChild->firstChild->tagName )
400                                {
401                                        $pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
402                                        if ( $this->debug ) $this->log ( "403 error during CREATE for node '" . $pubnode . "' ");
403                                        $name =  preg_replace ( '/^.*?\/' . $this->username . '\//','', $pubnode );
404                                        $newnode = '';
405                                        if ( ! in_array ( 'create', $this->pubsubNext ) )
406                                        {
407                                                $a = array ( );
408                                                foreach ( explode ( '/', $name ) as $v )
409                                                {
410                                                        $newnode .= '/' . $v;
411                                                        $a[] = array (
412                                                                'call' => 'create',
413                                                                'to' => $node->getAttribute ( 'from' ),
414                                                                'name' => $newnode );
415                                                }
416                                                foreach ( array_reverse ( $a ) as $v )
417                                                        array_unshift ( $this->pubsubNext, $v );
418                                                $this->pubsubDoNext ( );
419                                        }
420                                }
421                        }
422                        elseif ( $errnode->length > 0 && '404' == $errnode->item( 0 )->getAttribute ( 'code' ) )
423                        {
424                                if ( 'PUBLISH' == $node->firstChild->firstChild->tagName )
425                                {
426                                        $pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
427                                        if ( $this->debug ) $this->log ( "404 error during PUBLISH for node '" . $pubnode . "' ");
428                                        $publish = $this->query ( '//*/PUBLISH', $node );
429                                        $this->pubsubNext[] = array (
430                                                'call' => 'publish',
431                                                'to' => $node->getAttribute ( 'from' ),
432                                                'name' => preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) ,
433                                                'contents' => $publish->item( 0 )->firstChild->nodeValue );
434                                        if ( $this->debug ) $this->log ( "attempting to create node '" . $this->pubsubNext[0]['name'] . "' ");
435                                        $this->pubsubCreateNode ( $node->getAttribute ( 'from' ) ,'set', preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) );
436                                }
437                        }
438                        elseif ( $errnode->length > 0 && '409' == $errnode->item( 0 )->getAttribute ( 'code' ) )
439                        {
440                                if ( 'CANCEL' == $errnode->item( 0 )->firstChild->tagName || 'CONFLICT' == $errnode->item( 0 )->firstChild->tagName )
441                                        $this->pubsubDoNext ( );
442                        }
443                }
444                elseif ( 0 < count ( $this->pubsubNext ) )
445                        $this->pubsubDoNext ( );
446        }
447
448        // do next pubsub request
449        private function pubsubDoNext ( )
450        {
451                if ( 0 < count ( $this->pubsubNext ) )
452                {
453                        $pub = array_shift ( $this->pubsubNext );
454                        if ( 'publish' == $pub['call'] )
455                        {
456                                if ( $this->debug ) $this->log ( "attempting to publish to node '" . $pub['name'] . "'  contents '" . $pub['contents'] . "'");
457                                $this->pubsubPublish ( $pub[$to], 'set', $pub['name'], $pub['contents'] );
458                        }
459                        if ( 'create' == $pub['call'] )
460                        {
461                                if ( $this->debug ) $this->log ( "attempting to create node '" . $pub['name'] . "' ");
462                                $this->pubsubCreateNode ( $pub[$to], 'set', $pub['name'] );
463                        }
464                }
465        }
466
467        // do basic setup to get the connection logged in and going
468        private function initializeQueue ( )
469        {
470                $this->loggedIn = false;
471                $this->streamTagBegin = '<'."?xml version='1.0'?"."><stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
472                $this->streamTagEnd = '</stream:stream>';
473                $this->sendQueue[] = $this->streamTagBegin;
474                $this->recvHandlers['stream:features'] = 'handleFeatures' ;
475                $this->recvHandlers['features'] = 'handleFeatures' ;
476                $this->recvHandlers['proceed'] = 'enableTLS' ;
477        }
478
479        // send data out the socket
480        private function send ( $data )
481        {
482                $len = strlen ( $data );
483                if ( $this->debug ) $this->log ( "SEND: $data");
484                if ( false !== $this->connection )
485                {
486                        if ( fwrite ( $this->connection, $data, $len) === $len )
487                                return true;
488                        else
489                                return false;
490                }
491                return false;
492        }
493
494        // receive any data waiting on the socket
495        private function recv ()
496        {
497                if ( false !== $this->connection )
498                {
499                        $data = '';
500                        $data = fgets ( $this->connection, 4096 );
501                        if ( 4094 < strlen ( $data ) )
502                        {
503                                $count = 0;
504                                while ( 0 != strlen ( $moredata = fgets ( $this->connection, 1024 ) ) && 20 < $count++ )
505                                {
506                                        $data .= $moredata;
507                                        usleep ( 10 );
508                                }
509                        }
510                        if ( 0 < strlen ( $data ) )
511                        {
512                                $data = preg_replace ( '/^<\?xml version=\'1.0\'\?'.'>/', '', $data );
513                                $this->stream .= $data;
514                                if ( $this->debug ) $this->log ( "RECV: $data" );
515                                return $data;
516                        }
517                        else
518                                return false;
519                }
520                return false;
521        }
522
523        private function go ()
524        {
525                $this->recvQueue = implode ( '', $this->sendQueue );
526                $count = 0;
527                $this->moredata = false;
528                while ( false !== $this->connection )
529                {
530                        if ( 0 < count ( $this->sendQueue ) )
531                        {
532                                $count = 0;
533                                while ( $data = array_shift ( $this->sendQueue ) )
534                                        $this->send ( $data );
535                        }
536                        $data = $this->recv ( );
537                        xml_parse ( $this->xmlparser, $data, false );
538                        while ( $rnode = array_shift ( $this->recvTags ) )
539                        {
540                                $rname = strtolower ( $rnode->localName );
541                                if ( $this->debug ) $this->log ( " processing $rname ");
542                                if ( isset ( $this->recvHandlers[$rname] ) ) //&& is_callable ( $this->recvHandlers[$r->name] ) )
543                                {
544                                        if ( method_exists ( $this, $this->recvHandlers[$rname] ) )
545                                                call_user_func_array ( array ( $this, $this->recvHandlers[$rname] ),  array ( &$rnode ) );
546                                        else
547                                                call_user_func_array ( $this->recvHandlers[$rname], array ( &$rnode ) );
548                                }
549                        }
550                        $count++;
551                        if ( $count > 20 )
552                        {
553                                if ( $this->idle === true )
554                                {
555                                        $count = 0;
556                                        usleep ( 200 );
557                                }
558                                else
559                                {
560                                        if ( $this->ready == true && count ( $this->handleCommand ) <= count ( $this->command ) )
561                                        {
562                                                $count = 0;
563                                                return ;
564                                        }
565                                }
566                        }
567                        else
568                                usleep ( 20 );
569                }
570        }
571
572
573        // xml parser start element
574        private function startElement ( $parser, $name, $attrs )
575        {
576                $this->depth++;
577                $namespace = '';
578
579                if ( 'STREAM:STREAM' == $name )
580                        $this->processDepth++;
581                foreach ( $attrs as $k => $v )
582                        if ( preg_match ( '/^xmlns:?(.*)/i', $k, $matches ) )
583                        {
584                                if ( strlen ( $matches[1] ) > 0 && ! isset ( $this->namespaces [ $matches[1] ] ) )
585                                {
586                                        $this->xquery->registerNamespace ( $matches[1], $v );
587                                        $this->namespaces [ $matches[1] ] = $v;
588                                        $namespace = $v;
589                                        if ( $this->debug ) $this->log ( " adding namespace $k => $v ");
590                                }
591                        }
592                if ( $namespace != '' )
593                        $node = $this->doc->createElementNS ( $namespace, $name );
594                else
595                        $node = $this->doc->createElement ( $name );
596                foreach ( $attrs as $k => $v )
597                        $node->setAttribute ( strtolower ( $k ), $v );
598                $this->currentXMLNode = $this->currentXMLNode->appendChild ( $node );
599        }
600
601        // xml parser start element
602        private function endElement ( $parser, $name )
603        {
604                $this->depth--;
605                //if ( $this->debug ) $this->log ( "depth: " . $this->depth . " processDepth: " . $this->processDepth . " ");
606                if ( $this->depth == $this->processDepth || 'STREAM:STREAM' == $name || 'STREAM:FEATURES' == $name || 'PROCEED' == $name )
607                {
608                        if ( $this->debug ) $this->log ( " adding $name to tags to process ");
609                        array_push ( $this->recvTags, $this->currentXMLNode ); // replace with tag
610                }
611                $this->currentXMLNode = $this->currentXMLNode->parentNode;
612        }
613
614        // xml parser start element
615        private function parseData ( $parser, $text )
616        {
617                $this->currentXMLNode->appendChild ( $this->doc->createTextNode ( $text ) );
618        }
619
620        // xml parser start element
621        private function setupXmlParser ( )
622        {
623                $this->depth = 0;
624                $this->xmlparser = xml_parser_create ( );
625                xml_set_object ( $this->xmlparser, $this );
626                xml_set_element_handler ( $this->xmlparser, 'startElement', 'endElement' );
627                xml_set_character_data_handler ( $this->xmlparser, 'parseData' );
628                $this->doc = new DOMDocument ();
629                $this->xquery = new DOMXpath ( $this->doc );
630                $this->xquery->registerNamespace ( 'stream', 'http://etherx.jabber.org/streams' );
631                $this->currentXMLNode = $this->doc->appendChild ( $this->doc->createElement ( 'start' ) );
632        }
633
634        // xml XPath query
635        private function query ( $expression, &$node = '' )
636        {
637                if ( '' == $node )
638                        return $this->xquery->query     ( $expression );
639                else
640                        return $this->xquery->query     ( $expression , $node );
641        }
642
643
644        // open xmpp connection, will accept jid and password
645        public function open ( $jid = null, $password = null)
646        {
647                if ( null != $jid )
648                        $this->jid = $jid;
649                if ( null != $password )
650                        $this->password = $password;
651                $this->ready = false;
652                if ( false !== $this->connect () )
653                {
654                        sleep(2);
655                        $this->go ();
656                }
657                else
658                        return false;
659                return true;
660        }
661
662        public function close ()
663        {
664                if ( false !== $this->connection )
665                {
666                        $this->send ( '</stream:stream>');
667                        fclose ( $this->connection );
668                        $this->connection = false;
669                }
670        }
671
672        // add a send or recv handler, direction = [ send | recv ], command = command to handle, handler = function ref
673        public function addHandler ( $direction, $command, $handler )
674        {
675                if ( 'send' == $direction )
676                        $this->sendHandler[$command] = $handler;
677                if ( 'recv' == $direction )
678                        $this->recvHandler[$command] = $handler;
679        }
680
681        // handle logging
682        private function log ( $message )
683        {
684                error_log ( 'XMPP: ' . $message );
685                //echo  'XMPP: ' . $message . "\n";
686        }
687}
688
689
690/**
691        * * Log the action
692        * * @param string $action_type INSERT / UPDATE or DELETE
693        * * @param string $uid The UID of the modified item
694        * * @param integer $user_no The user owning the containing collection.
695        * * @param integer $collection_id The ID of the containing collection.
696        * * @param string $dav_name The DAV path of the item, relative to the DAViCal base path
697        * */
698function log_caldav_action( $action_type, $uid, $user_no, $collection_id, $dav_name )
699{
700  global $c;
701        $t = new xmpp();
702        $t->tls =  'none';
703        $t->idle =  false;
704        if ( 1 == $c->dbg["ALL"] || 1 == $c->dbg["push"] )
705                $t->debug = true ;
706        else
707                $t->debug = false ;
708        // for now use a flat node tree layout
709        $t->pubsubLayout = 'flat';
710        // get the principal_id for this collection, that's what the client will be looking for
711        $qry = new AwlQuery ('SELECT principal_id FROM principal JOIN collection USING (user_no) WHERE collection_id= :collection_id',
712                           array( ':collection_id' => $collection_id ) );
713        $qry->Exec('pubsub');
714        $row = $qry->Fetch();
715
716        $t->open ( $c->notifications_server['jid'], $c->notifications_server['password'] );
717        if ( isset ( $c->notifications_server['debug_jid'] ) )
718                $t->sendMessage ( $c->notifications_server['debug_jid'], "ACTION: $action_type\nUSER: $user_no\nDAV NAME: $dav_name\nPRINCIPAL ID: " . $row->principal_id );
719        $t->pubsubCreate ( '', 'set', '/davical-' . $row->principal_id, '<x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var="pubsub#access_model"><value>open</value></field><field var=\'pubsub#type\'>plist-apple<value></value></field></x>' );
720        $t->pubsubPublish ( '', 'set', '/davical-' . $row->principal_id , '<item xmlns="plist-apple" id="' . $uid . ' " ><plistfrag xmlns="plist-apple"><key>davical</key><string>' . $uid . '</string></plistfrag></item>', $uid );
721        $t->close();
722}
723
Note: See TracBrowser for help on using the repository browser.