2008-06-26 16:03:36 +01:00
#!/usr/bin/env php
2008-06-23 03:27:10 +01:00
< ? php
/*
* Laconica - a distributed open - source microblogging tool
* Copyright ( C ) 2008 , Controlez - Vous , Inc .
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < http :// www . gnu . org / licenses />.
*/
2008-06-23 04:05:23 +01:00
# Abort if called from a web server
if ( isset ( $_SERVER ) && array_key_exists ( 'REQUEST_METHOD' , $_SERVER )) {
print " This script must be run from the command line \n " ;
exit ();
}
2008-08-13 16:46:03 +01:00
define ( 'INSTALLDIR' , realpath ( dirname ( __FILE__ ) . '/..' ));
2008-06-23 03:27:10 +01:00
define ( 'LACONICA' , true );
2008-08-27 15:23:36 +01:00
2008-06-23 03:27:10 +01:00
require_once ( INSTALLDIR . '/lib/common.php' );
2008-06-26 08:07:03 +01:00
require_once ( INSTALLDIR . '/lib/jabber.php' );
2008-06-23 03:27:10 +01:00
2008-08-27 21:54:07 +01:00
set_error_handler ( 'common_error_handler' );
2008-06-26 16:12:02 +01:00
# This is kind of clunky; we create a class to call the global functions
# in jabber.php, which create a new XMPP class. A more elegant (?) solution
# might be to use make this a subclass of XMPP.
2008-06-23 03:27:10 +01:00
class XMPPDaemon {
2008-06-23 04:02:59 +01:00
2008-06-26 08:59:20 +01:00
function XMPPDaemon ( $resource = NULL ) {
2008-06-26 16:03:36 +01:00
static $attrs = array ( 'server' , 'port' , 'user' , 'password' , 'host' );
2008-06-23 04:09:30 +01:00
foreach ( $attrs as $attr )
{
2008-06-23 03:27:10 +01:00
$this -> $attr = common_config ( 'xmpp' , $attr );
}
2008-06-26 08:59:20 +01:00
if ( $resource ) {
$this -> resource = $resource ;
2008-06-26 16:03:36 +01:00
} else {
$this -> resource = common_config ( 'xmpp' , 'resource' ) . 'daemon' ;
2008-06-26 08:59:20 +01:00
}
2008-06-26 16:03:36 +01:00
2008-08-31 03:03:25 +01:00
$this -> log ( LOG_INFO , " INITIALIZE XMPPDaemon { $this -> user } @ { $this -> server } / { $this -> resource } " );
2008-06-23 03:27:10 +01:00
}
function connect () {
2008-07-05 21:24:12 +01:00
2008-06-26 16:03:36 +01:00
$connect_to = ( $this -> host ) ? $this -> host : $this -> server ;
$this -> log ( LOG_INFO , " Connecting to $connect_to on port $this->port " );
2008-09-02 02:26:04 +01:00
$this -> conn = jabber_connect ( $this -> resource );
2008-06-26 16:03:36 +01:00
2008-06-23 03:27:10 +01:00
if ( ! $this -> conn ) {
return false ;
}
2008-09-02 18:17:40 +01:00
2008-09-02 18:20:39 +01:00
$this -> conn -> setReconnectTimeout ( 600 );
2008-09-02 18:17:40 +01:00
2008-09-02 02:26:04 +01:00
jabber_send_presence ( " Send me a message to post a notice " , 'available' ,
NULL , 'available' , 100 );
2008-07-05 19:47:29 +01:00
return ! $this -> conn -> isDisconnected ();
2008-06-23 03:27:10 +01:00
}
2008-06-23 04:02:59 +01:00
2008-06-23 03:27:10 +01:00
function handle () {
2008-08-27 14:54:22 +01:00
$this -> conn -> addEventHandler ( 'message' , 'handle_message' , $this );
$this -> conn -> addEventHandler ( 'presence' , 'handle_presence' , $this );
2008-09-02 17:53:19 +01:00
$this -> conn -> addEventHandler ( 'reconnect' , 'handle_reconnect' , $this );
2008-08-30 05:07:17 +01:00
2008-08-27 22:04:29 +01:00
$this -> conn -> process ();
2008-06-23 03:27:10 +01:00
}
2008-07-20 06:57:02 +01:00
2008-09-02 17:53:19 +01:00
function handle_reconnect ( & $pl ) {
$this -> conn -> processUntil ( 'session_start' );
$this -> conn -> presence ( 'Send me a message to post a notice' , 'available' , NULL , 'available' , 100 );
}
2008-06-26 19:11:44 +01:00
function get_user ( $from ) {
$user = User :: staticGet ( 'jabber' , jabber_normalize_jid ( $from ));
return $user ;
}
2008-06-23 03:27:10 +01:00
function handle_message ( & $pl ) {
2008-06-23 23:36:41 +01:00
if ( $pl [ 'type' ] != 'chat' ) {
return ;
}
2008-08-25 18:31:05 +01:00
if ( mb_strlen ( $pl [ 'body' ]) == 0 ) {
2008-06-23 23:36:41 +01:00
return ;
}
2008-06-26 21:39:35 +01:00
$from = jabber_normalize_jid ( $pl [ 'from' ]);
2008-08-30 05:07:17 +01:00
# Forwarded from another daemon (probably a broadcaster) for
# us to handle
2008-08-30 09:56:07 +01:00
if ( $this -> is_self ( $from )) {
2008-08-30 05:07:17 +01:00
$from = $this -> get_ofrom ( $pl );
2008-08-30 09:56:07 +01:00
if ( is_null ( $from ) || $this -> is_self ( $from )) {
2008-08-30 05:07:17 +01:00
return ;
}
}
2008-06-26 21:39:35 +01:00
$user = $this -> get_user ( $from );
2008-06-23 03:27:10 +01:00
if ( ! $user ) {
2008-07-05 18:28:37 +01:00
$this -> from_site ( $from , 'Unknown user; go to ' .
2008-07-04 21:17:16 +01:00
common_local_url ( 'imsettings' ) .
' to add your address to your account' );
2008-06-23 05:18:16 +01:00
$this -> log ( LOG_WARNING , 'Message from unknown user ' . $from );
2008-06-23 03:27:10 +01:00
return ;
}
if ( $this -> handle_command ( $user , $pl [ 'body' ])) {
return ;
2008-07-08 01:30:55 +01:00
} else if ( $this -> is_autoreply ( $pl [ 'body' ])) {
$this -> log ( LOG_INFO , 'Ignoring auto reply from ' . $from );
return ;
2008-08-06 01:21:34 +01:00
} else if ( $this -> is_otr ( $pl [ 'body' ])) {
$this -> log ( LOG_INFO , 'Ignoring OTR from ' . $from );
return ;
2008-06-23 03:27:10 +01:00
} else {
2008-08-25 18:31:05 +01:00
$len = mb_strlen ( $pl [ 'body' ]);
if ( $len > 140 ) {
$this -> from_site ( $from , 'Message too long - maximum is 140 characters, you sent ' . $len );
2008-08-18 22:30:31 +01:00
return ;
}
2008-06-23 03:27:10 +01:00
$this -> add_notice ( $user , $pl );
}
}
2008-08-30 09:56:07 +01:00
function is_self ( $from ) {
return preg_match ( '/^' . strtolower ( jabber_daemon_address ()) . '/' , strtolower ( $from ));
}
2008-08-30 05:07:17 +01:00
function get_ofrom ( $pl ) {
$xml = $pl [ 'raw' ];
2008-08-30 06:28:10 +01:00
$addresses = $xml -> sub ( 'addresses' );
2008-08-30 05:07:17 +01:00
if ( ! $addresses ) {
$this -> log ( LOG_WARNING , 'Forwarded message without addresses' );
return NULL ;
}
2008-08-30 06:56:40 +01:00
$address = $addresses -> sub ( 'address' );
2008-08-30 05:07:17 +01:00
if ( ! $address ) {
$this -> log ( LOG_WARNING , 'Forwarded message without address' );
return NULL ;
}
2008-08-30 07:27:44 +01:00
if ( ! array_key_exists ( 'type' , $address -> attrs )) {
$this -> log ( LOG_WARNING , 'No type for forwarded message' );
return NULL ;
}
$type = $address -> attrs [ 'type' ];
2008-08-30 05:07:17 +01:00
if ( $type != 'ofrom' ) {
$this -> log ( LOG_WARNING , 'Type of forwarded message is not ofrom' );
return NULL ;
}
2008-08-30 07:27:44 +01:00
if ( ! array_key_exists ( 'jid' , $address -> attrs )) {
$this -> log ( LOG_WARNING , 'No jid for forwarded message' );
return NULL ;
}
$jid = $address -> attrs [ 'jid' ];
2008-08-30 06:59:38 +01:00
if ( ! $jid ) {
$this -> log ( LOG_WARNING , 'Could not get jid from address' );
return NULL ;
}
$this -> log ( LOG_DEBUG , 'Got message forwarded from jid ' . $jid );
return $jid ;
2008-08-30 05:07:17 +01:00
}
2008-07-08 01:30:55 +01:00
function is_autoreply ( $txt ) {
if ( preg_match ( '/[\[\(]?[Aa]uto-?[Rr]eply[\]\)]/' , $txt )) {
return true ;
} else {
return false ;
}
}
2008-07-20 06:57:02 +01:00
2008-08-06 01:21:34 +01:00
function is_otr ( $txt ) {
if ( preg_match ( '/^\?OTR/' , $txt )) {
return true ;
} else {
return false ;
}
}
2008-08-27 14:16:03 +01:00
2008-07-04 21:17:16 +01:00
function from_site ( $address , $msg ) {
$text = '[' . common_config ( 'site' , 'name' ) . '] ' . $msg ;
jabber_send_message ( $address , $text );
}
2008-07-05 18:28:37 +01:00
2008-06-23 03:27:10 +01:00
function handle_command ( $user , $body ) {
# XXX: localise
2008-08-22 20:10:32 +01:00
$p = explode ( ' ' , $body );
if ( count ( $p ) > 2 )
return false ;
switch ( $p [ 0 ]) {
case 'help' :
if ( count ( $p ) != 1 )
return false ;
$this -> from_site ( $user -> jabber , " Commands: \n on - turn on notifications \n off - turn off notifications \n help - show this help \n sub - subscribe to user \n unsub - unsubscribe from user " );
return true ;
2008-06-23 03:27:10 +01:00
case 'on' :
2008-08-22 20:10:32 +01:00
if ( count ( $p ) != 1 )
return false ;
2008-06-23 03:27:10 +01:00
$this -> set_notify ( $user , true );
2008-07-04 21:17:16 +01:00
$this -> from_site ( $user -> jabber , 'notifications on' );
2008-06-23 03:27:10 +01:00
return true ;
case 'off' :
2008-08-22 20:10:32 +01:00
if ( count ( $p ) != 1 )
return false ;
2008-06-23 03:27:10 +01:00
$this -> set_notify ( $user , false );
2008-07-04 21:17:16 +01:00
$this -> from_site ( $user -> jabber , 'notifications off' );
2008-06-23 03:27:10 +01:00
return true ;
2008-08-22 20:10:32 +01:00
case 'sub' :
if ( count ( $p ) == 1 ) {
$this -> from_site ( $user -> jabber , 'Specify the name of the user to subscribe to' );
return true ;
}
$result = subs_subscribe_user ( $user , $p [ 1 ]);
if ( $result == 'true' )
$this -> from_site ( $user -> jabber , 'Subscribed to ' . $p [ 1 ]);
else
$this -> from_site ( $user -> jabber , $result );
return true ;
case 'unsub' :
if ( count ( $p ) == 1 ) {
$this -> from_site ( $user -> jabber , 'Specify the name of the user to unsubscribe from' );
return true ;
}
$result = subs_unsubscribe_user ( $user , $p [ 1 ]);
if ( $result == 'true' )
$this -> from_site ( $user -> jabber , 'Unsubscribed from ' . $p [ 1 ]);
else
$this -> from_site ( $user -> jabber , $result );
return true ;
2008-06-23 03:27:10 +01:00
default :
return false ;
}
}
2008-06-23 03:38:59 +01:00
function set_notify ( & $user , $notify ) {
$orig = clone ( $user );
$user -> jabbernotify = $notify ;
$result = $user -> update ( $orig );
2008-08-19 08:13:21 +01:00
if ( ! $result ) {
2008-06-23 03:38:59 +01:00
$last_error = & PEAR :: getStaticProperty ( 'DB_DataObject' , 'lastError' );
2008-07-18 20:08:35 +01:00
$this -> log ( LOG_ERR ,
2008-06-23 03:38:59 +01:00
'Could not set notify flag to ' . $notify .
2008-06-23 04:02:59 +01:00
' for user ' . common_log_objstring ( $user ) .
2008-06-23 03:38:59 +01:00
': ' . $last_error -> message );
} else {
$this -> log ( LOG_INFO ,
'User ' . $user -> nickname . ' set notify flag to ' . $notify );
}
}
2008-06-23 04:02:59 +01:00
2008-06-23 03:27:10 +01:00
function add_notice ( & $user , & $pl ) {
2008-08-18 03:55:49 +01:00
$notice = Notice :: saveNew ( $user -> id , trim ( mb_substr ( $pl [ 'body' ], 0 , 140 )), 'xmpp' );
2008-07-30 03:28:56 +01:00
if ( is_string ( $notice )) {
$this -> log ( LOG_ERR , $notice );
2008-06-23 03:27:10 +01:00
return ;
}
2008-08-27 21:54:07 +01:00
common_broadcast_notice ( $notice );
2008-06-23 03:38:59 +01:00
$this -> log ( LOG_INFO ,
'Added notice ' . $notice -> id . ' from user ' . $user -> nickname );
2008-06-23 03:27:10 +01:00
}
2008-06-23 04:02:59 +01:00
2008-06-23 03:27:10 +01:00
function handle_presence ( & $pl ) {
2008-06-24 01:15:23 +01:00
$from = jabber_normalize_jid ( $pl [ 'from' ]);
2008-06-26 07:46:46 +01:00
switch ( $pl [ 'type' ]) {
2008-07-05 18:28:37 +01:00
case 'subscribe' :
# We let anyone subscribe
$this -> subscribed ( $from );
$this -> log ( LOG_INFO ,
'Accepted subscription from ' . $from );
break ;
case 'subscribed' :
case 'unsubscribed' :
case 'unsubscribe' :
$this -> log ( LOG_INFO ,
'Ignoring "' . $pl [ 'type' ] . '" from ' . $from );
break ;
default :
if ( ! $pl [ 'type' ]) {
$user = User :: staticGet ( 'jabber' , $from );
if ( ! $user ) {
2008-07-17 14:24:52 +01:00
$this -> log ( LOG_WARNING , 'Presence from unknown user ' . $from );
2008-07-05 18:28:37 +01:00
return ;
2008-06-26 07:46:46 +01:00
}
2008-07-05 18:28:37 +01:00
if ( $user -> updatefrompresence ) {
$this -> log ( LOG_INFO , 'Updating ' . $user -> nickname .
' status from presence.' );
$this -> add_notice ( $user , $pl );
}
}
break ;
2008-06-23 03:27:10 +01:00
}
}
2008-06-23 04:02:59 +01:00
2008-06-23 03:27:10 +01:00
function log ( $level , $msg ) {
common_log ( $level , 'XMPPDaemon(' . $this -> resource . '): ' . $msg );
}
2008-06-26 07:46:46 +01:00
function subscribed ( $to ) {
2008-06-26 16:03:36 +01:00
jabber_special_presence ( 'subscribed' , $to );
2008-06-26 07:46:46 +01:00
}
2008-06-23 03:27:10 +01:00
}
2008-08-31 03:38:44 +01:00
ini_set ( " max_execution_time " , " 0 " );
ini_set ( " max_input_time " , " 0 " );
set_time_limit ( 0 );
2008-08-18 04:30:49 +01:00
mb_internal_encoding ( 'UTF-8' );
2008-08-29 19:17:02 +01:00
$resource = ( $argc > 1 ) ? $argv [ 1 ] : ( common_config ( 'xmpp' , 'resource' ) . '-listen' );
2008-06-26 16:03:36 +01:00
$daemon = new XMPPDaemon ( $resource );
2008-06-23 03:27:10 +01:00
2008-06-23 04:01:50 +01:00
if ( $daemon -> connect ()) {
$daemon -> handle ();
}