2008-06-27 00:03:36 +09:00
#!/usr/bin/env php
2008-06-23 11:27:10 +09: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 12:05:23 +09: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-14 00:46:03 +09:00
define ( 'INSTALLDIR' , realpath ( dirname ( __FILE__ ) . '/..' ));
2008-06-23 11:27:10 +09:00
define ( 'LACONICA' , true );
2008-08-27 23:23:36 +09:00
2008-06-23 11:27:10 +09:00
require_once ( INSTALLDIR . '/lib/common.php' );
2008-06-26 16:07:03 +09:00
require_once ( INSTALLDIR . '/lib/jabber.php' );
2008-09-05 03:40:31 +09:00
require_once ( INSTALLDIR . '/lib/daemon.php' );
2008-06-23 11:27:10 +09:00
2008-08-28 05:54:07 +09:00
set_error_handler ( 'common_error_handler' );
2008-06-27 00:12:02 +09: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-09-05 03:40:31 +09:00
class XMPPDaemon extends Daemon {
2008-06-23 12:02:59 +09:00
2008-06-26 16:59:20 +09:00
function XMPPDaemon ( $resource = NULL ) {
2008-06-27 00:03:36 +09:00
static $attrs = array ( 'server' , 'port' , 'user' , 'password' , 'host' );
2008-06-23 12:09:30 +09:00
foreach ( $attrs as $attr )
{
2008-06-23 11:27:10 +09:00
$this -> $attr = common_config ( 'xmpp' , $attr );
}
2008-06-26 16:59:20 +09:00
if ( $resource ) {
$this -> resource = $resource ;
2008-06-27 00:03:36 +09:00
} else {
$this -> resource = common_config ( 'xmpp' , 'resource' ) . 'daemon' ;
2008-06-26 16:59:20 +09:00
}
2008-06-27 00:03:36 +09:00
2008-08-31 11:03:25 +09:00
$this -> log ( LOG_INFO , " INITIALIZE XMPPDaemon { $this -> user } @ { $this -> server } / { $this -> resource } " );
2008-06-23 11:27:10 +09:00
}
function connect () {
2008-07-06 05:24:12 +09:00
2008-06-27 00:03:36 +09:00
$connect_to = ( $this -> host ) ? $this -> host : $this -> server ;
$this -> log ( LOG_INFO , " Connecting to $connect_to on port $this->port " );
2008-09-02 10:26:04 +09:00
$this -> conn = jabber_connect ( $this -> resource );
2008-06-27 00:03:36 +09:00
2008-06-23 11:27:10 +09:00
if ( ! $this -> conn ) {
return false ;
}
2008-09-03 02:17:40 +09:00
2008-09-03 02:20:39 +09:00
$this -> conn -> setReconnectTimeout ( 600 );
2008-09-03 02:17:40 +09:00
2008-09-02 10:26:04 +09:00
jabber_send_presence ( " Send me a message to post a notice " , 'available' ,
NULL , 'available' , 100 );
2008-07-06 03:47:29 +09:00
return ! $this -> conn -> isDisconnected ();
2008-06-23 11:27:10 +09:00
}
2008-06-23 12:02:59 +09:00
2008-09-05 03:40:31 +09:00
function name () {
return strtolower ( 'xmppdaemon.' . $this -> resource );
}
function run () {
if ( $this -> connect ()) {
$this -> conn -> addEventHandler ( 'message' , 'handle_message' , $this );
$this -> conn -> addEventHandler ( 'presence' , 'handle_presence' , $this );
$this -> conn -> addEventHandler ( 'reconnect' , 'handle_reconnect' , $this );
$this -> conn -> process ();
}
2008-06-23 11:27:10 +09:00
}
2008-07-20 14:57:02 +09:00
2008-09-03 01:53:19 +09: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-27 03:11:44 +09:00
function get_user ( $from ) {
$user = User :: staticGet ( 'jabber' , jabber_normalize_jid ( $from ));
return $user ;
}
2008-06-23 11:27:10 +09:00
function handle_message ( & $pl ) {
2008-06-24 07:36:41 +09:00
if ( $pl [ 'type' ] != 'chat' ) {
return ;
}
2008-08-26 02:31:05 +09:00
if ( mb_strlen ( $pl [ 'body' ]) == 0 ) {
2008-06-24 07:36:41 +09:00
return ;
}
2008-06-27 05:39:35 +09:00
$from = jabber_normalize_jid ( $pl [ 'from' ]);
2008-08-30 13:07:17 +09:00
# Forwarded from another daemon (probably a broadcaster) for
# us to handle
2008-08-30 17:56:07 +09:00
if ( $this -> is_self ( $from )) {
2008-08-30 13:07:17 +09:00
$from = $this -> get_ofrom ( $pl );
2008-08-30 17:56:07 +09:00
if ( is_null ( $from ) || $this -> is_self ( $from )) {
2008-08-30 13:07:17 +09:00
return ;
}
}
2008-06-27 05:39:35 +09:00
$user = $this -> get_user ( $from );
2008-06-23 11:27:10 +09:00
if ( ! $user ) {
2008-07-06 02:28:37 +09:00
$this -> from_site ( $from , 'Unknown user; go to ' .
2008-07-05 05:17:16 +09:00
common_local_url ( 'imsettings' ) .
' to add your address to your account' );
2008-06-23 13:18:16 +09:00
$this -> log ( LOG_WARNING , 'Message from unknown user ' . $from );
2008-06-23 11:27:10 +09:00
return ;
}
if ( $this -> handle_command ( $user , $pl [ 'body' ])) {
return ;
2008-07-08 09:30:55 +09:00
} else if ( $this -> is_autoreply ( $pl [ 'body' ])) {
$this -> log ( LOG_INFO , 'Ignoring auto reply from ' . $from );
return ;
2008-08-06 09:21:34 +09:00
} else if ( $this -> is_otr ( $pl [ 'body' ])) {
$this -> log ( LOG_INFO , 'Ignoring OTR from ' . $from );
return ;
2008-06-23 11:27:10 +09:00
} else {
2008-08-26 02:31:05 +09: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-19 06:30:31 +09:00
return ;
}
2008-06-23 11:27:10 +09:00
$this -> add_notice ( $user , $pl );
}
2008-09-06 10:55:01 +09:00
$user -> free ();
unset ( $user );
2008-06-23 11:27:10 +09:00
}
2008-08-30 17:56:07 +09:00
function is_self ( $from ) {
return preg_match ( '/^' . strtolower ( jabber_daemon_address ()) . '/' , strtolower ( $from ));
}
2008-08-30 13:07:17 +09:00
function get_ofrom ( $pl ) {
$xml = $pl [ 'raw' ];
2008-08-30 14:28:10 +09:00
$addresses = $xml -> sub ( 'addresses' );
2008-08-30 13:07:17 +09:00
if ( ! $addresses ) {
$this -> log ( LOG_WARNING , 'Forwarded message without addresses' );
return NULL ;
}
2008-08-30 14:56:40 +09:00
$address = $addresses -> sub ( 'address' );
2008-08-30 13:07:17 +09:00
if ( ! $address ) {
$this -> log ( LOG_WARNING , 'Forwarded message without address' );
return NULL ;
}
2008-08-30 15:27:44 +09: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 13:07:17 +09:00
if ( $type != 'ofrom' ) {
$this -> log ( LOG_WARNING , 'Type of forwarded message is not ofrom' );
return NULL ;
}
2008-08-30 15:27:44 +09: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 14:59:38 +09: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 13:07:17 +09:00
}
2008-07-08 09:30:55 +09:00
function is_autoreply ( $txt ) {
if ( preg_match ( '/[\[\(]?[Aa]uto-?[Rr]eply[\]\)]/' , $txt )) {
return true ;
} else {
return false ;
}
}
2008-07-20 14:57:02 +09:00
2008-08-06 09:21:34 +09:00
function is_otr ( $txt ) {
if ( preg_match ( '/^\?OTR/' , $txt )) {
return true ;
} else {
return false ;
}
}
2008-08-27 22:16:03 +09:00
2008-07-05 05:17:16 +09:00
function from_site ( $address , $msg ) {
$text = '[' . common_config ( 'site' , 'name' ) . '] ' . $msg ;
jabber_send_message ( $address , $text );
}
2008-07-06 02:28:37 +09:00
2008-06-23 11:27:10 +09:00
function handle_command ( $user , $body ) {
# XXX: localise
2008-08-23 04:10:32 +09: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 11:27:10 +09:00
case 'on' :
2008-08-23 04:10:32 +09:00
if ( count ( $p ) != 1 )
return false ;
2008-06-23 11:27:10 +09:00
$this -> set_notify ( $user , true );
2008-07-05 05:17:16 +09:00
$this -> from_site ( $user -> jabber , 'notifications on' );
2008-06-23 11:27:10 +09:00
return true ;
case 'off' :
2008-08-23 04:10:32 +09:00
if ( count ( $p ) != 1 )
return false ;
2008-06-23 11:27:10 +09:00
$this -> set_notify ( $user , false );
2008-07-05 05:17:16 +09:00
$this -> from_site ( $user -> jabber , 'notifications off' );
2008-06-23 11:27:10 +09:00
return true ;
2008-08-23 04:10:32 +09: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 11:27:10 +09:00
default :
return false ;
}
}
2008-06-23 11:38:59 +09:00
function set_notify ( & $user , $notify ) {
$orig = clone ( $user );
$user -> jabbernotify = $notify ;
$result = $user -> update ( $orig );
2008-08-19 16:13:21 +09:00
if ( ! $result ) {
2008-06-23 11:38:59 +09:00
$last_error = & PEAR :: getStaticProperty ( 'DB_DataObject' , 'lastError' );
2008-07-19 04:08:35 +09:00
$this -> log ( LOG_ERR ,
2008-06-23 11:38:59 +09:00
'Could not set notify flag to ' . $notify .
2008-06-23 12:02:59 +09:00
' for user ' . common_log_objstring ( $user ) .
2008-06-23 11:38:59 +09:00
': ' . $last_error -> message );
} else {
$this -> log ( LOG_INFO ,
'User ' . $user -> nickname . ' set notify flag to ' . $notify );
}
}
2008-06-23 12:02:59 +09:00
2008-06-23 11:27:10 +09:00
function add_notice ( & $user , & $pl ) {
2008-08-18 11:55:49 +09:00
$notice = Notice :: saveNew ( $user -> id , trim ( mb_substr ( $pl [ 'body' ], 0 , 140 )), 'xmpp' );
2008-07-30 11:28:56 +09:00
if ( is_string ( $notice )) {
$this -> log ( LOG_ERR , $notice );
2008-06-23 11:27:10 +09:00
return ;
}
2008-08-28 05:54:07 +09:00
common_broadcast_notice ( $notice );
2008-06-23 11:38:59 +09:00
$this -> log ( LOG_INFO ,
'Added notice ' . $notice -> id . ' from user ' . $user -> nickname );
2008-09-06 10:55:01 +09:00
$notice -> free ();
unset ( $notice );
2008-06-23 11:27:10 +09:00
}
2008-06-23 12:02:59 +09:00
2008-06-23 11:27:10 +09:00
function handle_presence ( & $pl ) {
2008-06-24 09:15:23 +09:00
$from = jabber_normalize_jid ( $pl [ 'from' ]);
2008-06-26 15:46:46 +09:00
switch ( $pl [ 'type' ]) {
2008-07-06 02:28:37 +09: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 22:24:52 +09:00
$this -> log ( LOG_WARNING , 'Presence from unknown user ' . $from );
2008-07-06 02:28:37 +09:00
return ;
2008-06-26 15:46:46 +09:00
}
2008-07-06 02:28:37 +09:00
if ( $user -> updatefrompresence ) {
$this -> log ( LOG_INFO , 'Updating ' . $user -> nickname .
' status from presence.' );
$this -> add_notice ( $user , $pl );
}
2008-09-06 10:55:01 +09:00
$user -> free ();
unset ( $user );
2008-07-06 02:28:37 +09:00
}
break ;
2008-06-23 11:27:10 +09:00
}
}
2008-06-23 12:02:59 +09:00
2008-06-23 11:27:10 +09:00
function log ( $level , $msg ) {
common_log ( $level , 'XMPPDaemon(' . $this -> resource . '): ' . $msg );
}
2008-06-26 15:46:46 +09:00
function subscribed ( $to ) {
2008-06-27 00:03:36 +09:00
jabber_special_presence ( 'subscribed' , $to );
2008-06-26 15:46:46 +09:00
}
2008-06-23 11:27:10 +09:00
}
2008-08-31 11:38:44 +09:00
ini_set ( " max_execution_time " , " 0 " );
ini_set ( " max_input_time " , " 0 " );
set_time_limit ( 0 );
2008-08-18 12:30:49 +09:00
mb_internal_encoding ( 'UTF-8' );
2008-08-30 03:17:02 +09:00
$resource = ( $argc > 1 ) ? $argv [ 1 ] : ( common_config ( 'xmpp' , 'resource' ) . '-listen' );
2008-06-27 00:03:36 +09:00
$daemon = new XMPPDaemon ( $resource );
2008-06-23 11:27:10 +09:00
2008-09-05 03:40:31 +09:00
$daemon -> runOnce ();