Merge branch '0.8.x' into design_reset

* 0.8.x: (32 commits)
  admin indicators in groups
  show section with admins in sidebar of group
  update to latest (r76) version of XMPPHP
  better output for common error handler
  fix logging error
  note when going background
  change name of constructor for xmppdaemon
  add a lot more logging to xmppdaemon
  error in get_option_value wasn't returning a value
  reformat commandline.inc
  if not in daemon mode, xmppdaemon sends log to stdout
  extract log-line formatting to its own function
  got my background/foreground logic backwards
  twitter status fetcher takes an id argument
  more efficient fixup of conversations
  commandline processing handles errors better
  xmppdaemon.php can stay in foreground
  command line arg handling a little more flexible
  Daemon can optionally not go into the background
  don't canonicalize people's text into URLs
  ...

Conflicts:
	theme/base/css/display.css
This commit is contained in:
Zach Copley 2009-06-29 13:23:45 -07:00
commit dd1fc46f09
23 changed files with 559 additions and 117 deletions

12
README
View File

@ -1278,6 +1278,18 @@ type: type of search. Ignored if PostgreSQL or Sphinx are enabled. Can either
systems. We'll probably add another type sometime in the future,
with our own indexing system (maybe like MediaWiki's).
sessions
--------
Session handling.
handle: boolean. Whether we should register our own PHP session-handling
code (using the database and memcache if enabled). Defaults to false.
Setting this to true makes some sense on large or multi-server
sites, but it probably won't hurt for smaller ones, either.
debug: whether to output debugging info for session storage. Can help
with weird session bugs, sometimes. Default false.
Troubleshooting
===============

View File

@ -67,7 +67,9 @@ class ApiAction extends Action
$this->process_command();
} else {
# basic authentication failed
common_log(LOG_WARNING, "Failed API auth attempt, nickname: $nickname.");
list($proxy, $ip) = common_client_ip();
common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
$this->show_basic_auth_error();
}
}

View File

@ -167,6 +167,15 @@ class GroupMemberListItem extends ProfileListItem
$this->group = $group;
}
function showFullName()
{
parent::showFullName();
if ($this->profile->isAdmin($this->group)) {
$this->out->text(' ');
$this->out->element('span', 'admin_indicator', _('Admin'));
}
}
function showActions()
{
$this->startActions();

View File

@ -331,6 +331,7 @@ class ShowgroupAction extends GroupDesignAction
{
$this->showMembers();
$this->showStatistics();
$this->showAdmins();
$cloud = new GroupTagCloudSection($this, $this->group);
$cloud->show();
}
@ -369,6 +370,18 @@ class ShowgroupAction extends GroupDesignAction
$this->elementEnd('div');
}
/**
* Show list of admins
*
* @return void
*/
function showAdmins()
{
$adminSection = new GroupAdminSection($this, $this->group);
$adminSection->show();
}
/**
* Show some statistics
*
@ -423,3 +436,34 @@ class ShowgroupAction extends GroupDesignAction
$this->elementEnd('div');
}
}
class GroupAdminSection extends ProfileSection
{
var $group;
function __construct($out, $group)
{
parent::__construct($out);
$this->group = $group;
}
function getProfiles()
{
return $this->group->getAdmins();
}
function title()
{
return _('Admins');
}
function divId()
{
return 'group_admins';
}
function moreUrl()
{
return null;
}
}

View File

@ -874,7 +874,6 @@ class Notice extends Memcached_DataObject
$qry .= '('.$id.', '.$this->id.', '.$source.', "'.$this->created.'") ';
$cnt++;
if ($cnt >= MAX_BOXCARS) {
common_debug($qry);
$inbox = new Notice_inbox();
$inbox->query($qry);
$qry = $qryhdr;
@ -883,7 +882,6 @@ class Notice extends Memcached_DataObject
}
if ($cnt > 0) {
common_debug($qry);
$inbox = new Notice_inbox();
$inbox->query($qry);
}

129
classes/Session.php Normal file
View File

@ -0,0 +1,129 @@
<?php
/**
* Table Definition for session
*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2009, Control Yourself, 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/>.
*/
if (!defined('LACONICA')) { exit(1); }
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Session extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'session'; // table name
public $id; // varchar(32) primary_key not_null
public $session_data; // text()
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/* Static get */
function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Session',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
static function logdeb($msg)
{
if (common_config('sessions', 'debug')) {
common_debug("Session: " . $msg);
}
}
static function open($save_path, $session_name)
{
return true;
}
static function close()
{
return true;
}
static function read($id)
{
self::logdeb("Fetching session '$id'");
$session = Session::staticGet('id', $id);
if (empty($session)) {
return '';
} else {
return (string)$session->session_data;
}
}
static function write($id, $session_data)
{
self::logdeb("Writing session '$id'");
$session = Session::staticGet('id', $id);
if (empty($session)) {
$session = new Session();
$session->id = $id;
$session->session_data = $session_data;
$session->created = common_sql_now();
return $session->insert();
} else {
$session->session_data = $session_data;
return $session->update();
}
}
static function destroy($id)
{
self::logdeb("Deleting session $id");
$session = Session::staticGet('id', $id);
if (!empty($session)) {
return $session->delete();
}
}
static function gc($maxlifetime)
{
self::logdeb("garbage collection (maxlifetime = $maxlifetime)");
$epoch = time() - $maxlifetime;
$qry = 'DELETE FROM session ' .
'WHERE modified < "'.$epoch.'"';
$session = new Session();
$result = $session->query($qry);
self::logdeb("garbage collection result = $result");
}
static function setSaveHandler()
{
self::logdeb("setting save handlers");
$result = session_set_save_handler('Session::open', 'Session::close', 'Session::read',
'Session::write', 'Session::destroy', 'Session::gc');
self::logdeb("save handlers result = $result");
return $result;
}
}

View File

@ -132,6 +132,13 @@ class Status_network extends DB_DataObject
}
} else {
$sn = self::memGet('hostname', strtolower($servername));
if (empty($sn)) {
// Try for a no-www address
if (0 == strncasecmp($servername, 'www.', 4)) {
$sn = self::memGet('hostname', strtolower(substr($servername, 4)));
}
}
}
if (!empty($sn)) {

View File

@ -126,6 +126,30 @@ class User_group extends Memcached_DataObject
return $members;
}
function getAdmins($offset=0, $limit=null)
{
$qry =
'SELECT profile.* ' .
'FROM profile JOIN group_member '.
'ON profile.id = group_member.profile_id ' .
'WHERE group_member.group_id = %d ' .
'AND group_member.is_admin = 1 ' .
'ORDER BY group_member.modified ASC ';
if ($limit != null) {
if (common_config('db','type') == 'pgsql') {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
} else {
$qry .= ' LIMIT ' . $offset . ', ' . $limit;
}
}
$admins = new Profile();
$admins->query(sprintf($qry, $this->id));
return $admins;
}
function getBlocked($offset=0, $limit=null)
{
$qry =

9
classes/laconica.ini Executable file → Normal file
View File

@ -380,6 +380,15 @@ replied_id = 1
notice_id = K
profile_id = K
[session]
id = 130
session_data = 34
created = 142
modified = 384
[session__keys]
id = K
[sms_carrier]
id = 129
name = 2

View File

@ -524,3 +524,14 @@ create table group_alias (
index group_alias_group_id_idx (group_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table session (
id varchar(32) primary key comment 'session ID',
session_data text comment 'session data',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
index session_modified_idx (modified)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;

View File

@ -27,7 +27,7 @@
*/
/** XMPPHP_XMLStream */
require_once "XMPP.php";
require_once dirname(__FILE__) . "/XMPP.php";
/**
* XMPPHP Main Class

View File

@ -27,13 +27,13 @@
*/
/** XMPPHP_Exception */
require_once 'Exception.php';
require_once dirname(__FILE__) . '/Exception.php';
/** XMPPHP_XMLObj */
require_once 'XMLObj.php';
require_once dirname(__FILE__) . '/XMLObj.php';
/** XMPPHP_Log */
require_once 'Log.php';
require_once dirname(__FILE__) . '/Log.php';
/**
* XMPPHP XML Stream
@ -375,7 +375,7 @@ class XMPPHP_XMLStream {
* integer -> process for this amount of time
*/
private function __process($maximum=0) {
private function __process($maximum=5) {
$remaining = $maximum;

View File

@ -27,8 +27,8 @@
*/
/** XMPPHP_XMLStream */
require_once "XMLStream.php";
require_once "Roster.php";
require_once dirname(__FILE__) . "/XMLStream.php";
require_once dirname(__FILE__) . "/Roster.php";
/**
* XMPPHP Main Class
@ -208,6 +208,15 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
$this->send($out);
}
/**
* Send Auth request
*
* @param string $jid
*/
public function subscribe($jid) {
$this->send("<presence type='subscribe' to='{$jid}' from='{$this->fulljid}' />");
#$this->send("<presence type='subscribed' to='{$jid}' from='{$this->fulljid}' />");
}
/**
* Message handler

View File

@ -73,13 +73,45 @@ function handleError($error)
exit(-1);
}
function checkMirror($action_obj)
{
global $config;
static $alwaysRW = array('session', 'remember_me');
if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) {
if (is_array(common_config('db', 'mirror'))) {
// "load balancing", ha ha
$arr = common_config('db', 'mirror');
$k = array_rand($arr);
$mirror = $arr[$k];
} else {
$mirror = common_config('db', 'mirror');
}
// We ensure that these tables always are used
// on the master DB
$config['db']['database_rw'] = $config['db']['database'];
$config['db']['ini_rw'] = INSTALLDIR.'/classes/laconica.ini';
foreach ($alwaysRW as $table) {
$config['db']['table_'.$table] = 'rw';
}
// everyone else uses the mirror
$config['db']['database'] = $mirror;
}
}
function main()
{
// quick check for fancy URL auto-detection support in installer.
if (isset($_SERVER['REDIRECT_URL']) && ((dirname($_SERVER['REQUEST_URI']) . '/check-fancy') === $_SERVER['REDIRECT_URL'])) {
die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs.");
}
global $user, $action, $config;
global $user, $action;
Snapshot::check();
@ -146,19 +178,7 @@ function main()
} else {
$action_obj = new $action_class();
// XXX: find somewhere for this little block to live
if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) {
if (is_array(common_config('db', 'mirror'))) {
// "load balancing", ha ha
$arr = common_config('db', 'mirror');
$k = array_rand($arr);
$mirror = $arr[$k];
} else {
$mirror = common_config('db', 'mirror');
}
$config['db']['database'] = $mirror;
}
checkMirror($action_obj);
try {
if ($action_obj->prepare($args)) {

View File

@ -254,6 +254,9 @@ $config =
'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
'search' =>
array('type' => 'fulltext'),
'sessions' =>
array('handle' => false, // whether to handle sessions ourselves
'debug' => false), // debugging output for sessions
);
$config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');

View File

@ -23,6 +23,13 @@ if (!defined('LACONICA')) {
class Daemon
{
var $daemonize = true;
function __construct($daemonize = true)
{
$this->daemonize = $daemonize;
}
function name()
{
return null;
@ -129,12 +136,16 @@ class Daemon
common_log(LOG_INFO, $this->name() . ' already running. Exiting.');
exit(0);
}
if ($this->background()) {
$this->writePidFile();
$this->changeUser();
$this->run();
$this->clearPidFile();
if ($this->daemonize) {
common_log(LOG_INFO, 'Backgrounding daemon "'.$this->name().'"');
$this->background();
}
$this->writePidFile();
$this->changeUser();
$this->run();
$this->clearPidFile();
}
function run()

View File

@ -27,11 +27,12 @@ require_once(INSTALLDIR.'/classes/Notice.php');
class QueueHandler extends Daemon
{
var $_id = 'generic';
function QueueHandler($id=null)
function __construct($id=null, $daemonize=true)
{
parent::__construct($daemonize);
if ($id) {
$this->set_id($id);
}

View File

@ -139,8 +139,23 @@ function common_have_session()
function common_ensure_session()
{
$c = null;
if (array_key_exists(session_name, $_COOKIE)) {
$c = $_COOKIE[session_name()];
}
if (!common_have_session()) {
if (common_config('sessions', 'handle')) {
common_log(LOG_INFO, "Using our own session handler");
Session::setSaveHandler();
}
@session_start();
if (!isset($_SESSION['started'])) {
$_SESSION['started'] = time();
if (!empty($c)) {
common_log(LOG_WARNING, 'Session cookie "' . $_COOKIE[session_name()] . '" ' .
' is set but started value is null');
}
}
}
}
@ -485,17 +500,19 @@ function common_linkify($url) {
// It comes in special'd, so we unspecial it before passing to the stringifying
// functions
$url = htmlspecialchars_decode($url);
$display = File_redirection::_canonUrl($url);
$canon = File_redirection::_canonUrl($url);
$longurl_data = File_redirection::where($url);
if (is_array($longurl_data)) {
$longurl = $longurl_data['url'];
} elseif (is_string($longurl_data)) {
$longurl = $longurl_data;
} else {
die('impossible to linkify');
throw new ServerException("Can't linkify url '$url'");
}
$attrs = array('href' => $longurl, 'rel' => 'external');
$attrs = array('href' => $canon, 'rel' => 'external');
$is_attachment = false;
$attachment_id = null;
@ -513,13 +530,13 @@ function common_linkify($url) {
}
}
// if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
// where ID is the id of the attachment for the given URL.
//
// we need a better test telling what can be shown as an attachment
// we're currently picking up oembeds only.
// I think the best option is another file_view table in the db
// and associated dbobject.
// if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
// where ID is the id of the attachment for the given URL.
//
// we need a better test telling what can be shown as an attachment
// we're currently picking up oembeds only.
// I think the best option is another file_view table in the db
// and associated dbobject.
$query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
$file = new File;
@ -549,7 +566,7 @@ function common_linkify($url) {
$attrs['id'] = "attachment-{$attachment_id}";
}
return XMLStringer::estring('a', $attrs, $display);
return XMLStringer::estring('a', $attrs, $url);
}
function common_shorten_links($text)
@ -817,7 +834,12 @@ function common_date_iso8601($dt)
function common_sql_now()
{
return strftime('%Y-%m-%d %H:%M:%S', time());
return common_sql_date(time());
}
function common_sql_date($datetime)
{
return strftime('%Y-%m-%d %H:%M:%S', $datetime);
}
function common_redirect($url, $code=307)
@ -1082,15 +1104,20 @@ function common_ensure_syslog()
}
}
function common_log_line($priority, $msg)
{
static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR',
'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
return date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n";
}
function common_log($priority, $msg, $filename=null)
{
$logfile = common_config('site', 'logfile');
if ($logfile) {
$log = fopen($logfile, "a");
if ($log) {
static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR',
'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
$output = date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n";
$output = common_log_line($priority, $msg);
fwrite($log, $output);
fclose($log);
}
@ -1321,18 +1348,39 @@ function common_canonical_sms($sms)
function common_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
{
switch ($errno) {
case E_ERROR:
case E_COMPILE_ERROR:
case E_CORE_ERROR:
case E_USER_ERROR:
common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline)");
exit(1);
case E_PARSE:
case E_RECOVERABLE_ERROR:
common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline) [ABORT]");
die();
break;
case E_WARNING:
case E_COMPILE_WARNING:
case E_CORE_WARNING:
case E_USER_WARNING:
common_log(LOG_WARNING, "[$errno] $errstr ($errfile:$errline)");
break;
case E_NOTICE:
case E_USER_NOTICE:
common_log(LOG_NOTICE, "[$errno] $errstr ($errfile:$errline)");
break;
case E_STRICT:
case E_DEPRECATED:
case E_USER_DEPRECATED:
// XXX: config variable to log this stuff, too
break;
default:
common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline) [UNKNOWN LEVEL, die()'ing]");
die();
break;
}
// FIXME: show error page if we're on the Web
@ -1471,3 +1519,27 @@ function common_shorten_url($long_url)
return $short_url;
}
function common_client_ip()
{
if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
return null;
}
if ($_SERVER['HTTP_X_FORWARDED_FOR']) {
if ($_SERVER['HTTP_CLIENT_IP']) {
$proxy = $_SERVER['HTTP_CLIENT_IP'];
} else {
$proxy = $_SERVER['REMOTE_ADDR'];
}
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
if ($_SERVER['HTTP_CLIENT_IP']) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
}
return array($ip, $proxy);
}

View File

@ -63,14 +63,21 @@ if (isset($longoptions)) {
$parser = new Console_Getopt();
list($options, $args) = $parser->getopt($argv, $shortoptions, $longoptions);
$result = $parser->getopt($argv, $shortoptions, $longoptions);
if (PEAR::isError($result)) {
print $result->getMessage()."\n";
exit(1);
} else {
list($options, $args) = $result;
}
function show_help()
{
global $helptext;
$_default_help_text = <<<END_OF_DEFAULT
General options:
General options:
-q --quiet Quiet (little output)
-v --verbose Verbose (lots of output)
@ -80,11 +87,11 @@ General options:
-h --help Show this message and quit.
END_OF_DEFAULT;
if (isset($helptext)) {
print $helptext;
}
print $_default_help_text;
exit(0);
if (isset($helptext)) {
print $helptext;
}
print $_default_help_text;
exit(0);
}
foreach ($options as $option) {
@ -115,24 +122,53 @@ require_once INSTALLDIR . '/lib/common.php';
set_error_handler('common_error_handler');
function have_option($str)
function _make_matches($opt, $alt)
{
global $options;
foreach ($options as $option) {
if ($option[0] == $str) {
return true;
}
}
return false;
$matches = array();
if (strlen($opt) > 1 && 0 != strncmp($opt, '--', 2)) {
$matches[] = '--'.$opt;
} else {
$matches[] = $opt;
}
if (!empty($alt)) {
if (strlen($alt) > 1 && 0 != strncmp($alt, '--', 2)) {
$matches[] = '--'.$alt;
} else {
$matches[] = $alt;
}
}
return $matches;
}
function get_option_value($str)
function have_option($opt, $alt=null)
{
global $options;
foreach ($options as $option) {
if ($option[0] == $str) {
return $option[1];
}
}
return null;
global $options;
$matches = _make_matches($opt, $alt);
foreach ($options as $option) {
if (in_array($option[0], $matches)) {
return true;
}
}
return false;
}
function get_option_value($opt, $alt=null)
{
global $options;
$matches = _make_matches($opt, $alt);
foreach ($options as $option) {
if (in_array($option[0], $matches)) {
return $option[1];
}
}
return null;
}

View File

@ -24,22 +24,17 @@ require_once INSTALLDIR.'/scripts/commandline.inc';
common_log(LOG_INFO, 'Fixing up conversations.');
$notice = new Notice();
$notice->whereAdd('conversation is null');
$notice->orderBy('id');
$nid = new Notice();
$nid->query('select id, reply_to from notice where conversation is null');
$cnt = $notice->find();
while ($nid->fetch()) {
print "Found $cnt notices.\n";
$cid = null;
while ($notice->fetch()) {
$notice = new Notice();
print "$notice->id =>";
$orig = clone($notice);
if (empty($notice->reply_to)) {
$notice->conversation = $notice->id;
if (empty($nid->reply_to)) {
$cid = $nid->id;
} else {
$reply = Notice::staticGet('id', $notice->reply_to);
@ -52,6 +47,9 @@ while ($notice->fetch()) {
} else {
$notice->conversation = $reply->conversation;
}
unset($reply);
$reply = null;
}
print "$notice->conversation";
@ -63,5 +61,10 @@ while ($notice->fetch()) {
continue;
}
$notice = null;
$orig = null;
unset($notice);
unset($orig);
print ".\n";
}

View File

@ -25,9 +25,14 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
define('MAXCHILDREN', 2);
define('POLL_INTERVAL', 60); // in seconds
$shortoptions = 'i::';
$longoptions = array('id::');
$helptext = <<<END_OF_TRIM_HELP
Batch script for retrieving Twitter messages from foreign service.
-i --id Identity (default 'generic')
END_OF_TRIM_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
@ -64,7 +69,7 @@ class TwitterStatusFetcher extends Daemon
function name()
{
return ('twitterstatusfetcher.generic');
return ('twitterstatusfetcher.'.$this->_id);
}
/**
@ -625,6 +630,16 @@ class TwitterStatusFetcher extends Daemon
declare(ticks = 1);
$fetcher = new TwitterStatusFetcher();
if (have_option('i')) {
$id = get_option_value('i');
} else if (have_option('--id')) {
$id = get_option_value('--id');
} else if (count($args) > 0) {
$id = $args[0];
} else {
$id = null;
}
$fetcher = new TwitterStatusFetcher($id);
$fetcher->runOnce();

View File

@ -20,13 +20,14 @@
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
$shortoptions = 'i::';
$longoptions = array('id::');
$shortoptions = 'fi::';
$longoptions = array('id::', 'foreground');
$helptext = <<<END_OF_XMPP_HELP
Daemon script for receiving new notices from Jabber users.
-i --id Identity (default none)
-f --foreground Stay in the foreground (default background)
END_OF_XMPP_HELP;
@ -42,8 +43,10 @@ require_once INSTALLDIR . '/lib/daemon.php';
class XMPPDaemon extends Daemon
{
function XMPPDaemon($resource=null)
function __construct($resource=null, $daemonize=true)
{
parent::__construct($daemonize);
static $attrs = array('server', 'port', 'user', 'password', 'host');
foreach ($attrs as $attr)
@ -62,7 +65,6 @@ class XMPPDaemon extends Daemon
function connect()
{
$connect_to = ($this->host) ? $this->host : $this->server;
$this->log(LOG_INFO, "Connecting to $connect_to on port $this->port");
@ -73,10 +75,17 @@ class XMPPDaemon extends Daemon
return false;
}
$this->log(LOG_INFO, "Connected");
$this->conn->setReconnectTimeout(600);
$this->log(LOG_INFO, "Sending initial presence.");
jabber_send_presence("Send me a message to post a notice", 'available',
null, 'available', 100);
$this->log(LOG_INFO, "Done connecting.");
return !$this->conn->isDisconnected();
}
@ -89,17 +98,23 @@ class XMPPDaemon extends Daemon
{
if ($this->connect()) {
$this->log(LOG_DEBUG, "Initializing stanza handlers.");
$this->conn->addEventHandler('message', 'handle_message', $this);
$this->conn->addEventHandler('presence', 'handle_presence', $this);
$this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
$this->log(LOG_DEBUG, "Beginning processing loop.");
$this->conn->process();
}
}
function handle_reconnect(&$pl)
{
$this->log(LOG_DEBUG, "Got reconnection callback.");
$this->conn->processUntil('session_start');
$this->log(LOG_DEBUG, "Sending reconnection presence.");
$this->conn->presence('Send me a message to post a notice', 'available', null, 'available', 100);
}
@ -111,21 +126,27 @@ class XMPPDaemon extends Daemon
function handle_message(&$pl)
{
$from = jabber_normalize_jid($pl['from']);
if ($pl['type'] != 'chat') {
return;
}
if (mb_strlen($pl['body']) == 0) {
$this->log(LOG_WARNING, "Ignoring message of type ".$pl['type']." from $from.");
return;
}
$from = jabber_normalize_jid($pl['from']);
if (mb_strlen($pl['body']) == 0) {
$this->log(LOG_WARNING, "Ignoring message with empty body from $from.");
return;
}
# Forwarded from another daemon (probably a broadcaster) for
# us to handle
if ($this->is_self($from)) {
$this->log(LOG_INFO, "Got forwarded notice from self ($from).");
$from = $this->get_ofrom($pl);
$this->log(LOG_INFO, "Originally sent by $from.");
if (is_null($from) || $this->is_self($from)) {
$this->log(LOG_INFO, "Ignoring notice originally sent by $from.");
return;
}
}
@ -140,6 +161,7 @@ class XMPPDaemon extends Daemon
return;
}
if ($this->handle_command($user, $pl['body'])) {
$this->log(LOG_INFO, "Command messag by $from handled.");
return;
} else if ($this->is_autoreply($pl['body'])) {
$this->log(LOG_INFO, 'Ignoring auto reply from ' . $from);
@ -148,12 +170,20 @@ class XMPPDaemon extends Daemon
$this->log(LOG_INFO, 'Ignoring OTR from ' . $from);
return;
} else if ($this->is_direct($pl['body'])) {
$this->log(LOG_INFO, 'Got a direct message ' . $from);
preg_match_all('/d[\ ]*([a-z0-9]{1,64})/', $pl['body'], $to);
$to = preg_replace('/^d([\ ])*/', '', $to[0][0]);
$body = preg_replace('/d[\ ]*('. $to .')[\ ]*/', '', $pl['body']);
$this->log(LOG_INFO, 'Direct message from '. $user->nickname . ' to ' . $to);
$this->add_direct($user, $body, $to, $from);
} else {
$this->log(LOG_INFO, 'Posting a notice from ' . $user->nickname);
$this->add_notice($user, $pl);
}
@ -261,6 +291,7 @@ class XMPPDaemon extends Daemon
$notice = Notice::saveNew($user->id, $content_shortened, 'xmpp');
if (is_string($notice)) {
$this->log(LOG_ERR, $notice);
$this->from_site($user->jabber, $notice);
return;
}
common_broadcast_notice($notice);
@ -307,7 +338,14 @@ class XMPPDaemon extends Daemon
function log($level, $msg)
{
common_log($level, 'XMPPDaemon('.$this->resource.'): '.$msg);
$text = 'XMPPDaemon('.$this->resource.'): '.$msg;
common_log($level, $text);
if (!$this->daemonize)
{
$line = common_log_line($level, $text);
echo $line;
echo "\n";
}
}
function subscribed($to)
@ -323,16 +361,16 @@ if (common_config('xmpp','enabled')==false) {
exit();
}
if (have_option('i')) {
$id = get_option_value('i');
} else if (have_option('--id')) {
$id = get_option_value('--id');
if (have_option('i', 'id')) {
$id = get_option_value('i', 'id');
} else if (count($args) > 0) {
$id = $args[0];
} else {
$id = null;
}
$daemon = new XMPPDaemon($id);
$foreground = have_option('f', 'foreground');
$daemon = new XMPPDaemon($id, !$foreground);
$daemon->runOnce();

View File

@ -273,7 +273,6 @@ clear:both;
margin-bottom:18px;
}
#anon_notice {
float:left;
width:43.2%;
@ -288,7 +287,6 @@ font-size:1.1em;
font-weight:bold;
}
#footer {
float:left;
width:64%;
@ -600,7 +598,6 @@ display:none;
}
/* entity_profile */
/*entity_actions*/
.entity_actions {
float:right;
@ -729,7 +726,6 @@ margin-bottom:0;
min-height:60px;
}
.profile .form_group_join legend,
.profile .form_group_leave legend,
.profile .form_user_subscribe legend,
@ -764,13 +760,11 @@ display:inline;
margin-right:11px;
}
.profile .entity_profile .form_subscription_edit label {
font-weight:normal;
margin-right:11px;
}
/* NOTICE */
.notice,
.profile {
@ -793,7 +787,6 @@ width:95%;
float:left;
}
/* NOTICES */
#notices_primary {
float:left;
@ -965,7 +958,6 @@ border:0;
padding:0;
}
.notice .attachment {
position:relative;
padding-left:16px;
@ -1062,7 +1054,6 @@ margin-bottom:18px;
padding-left:20px;
}
#filter_tags {
margin-bottom:11px;
float:left;
@ -1108,8 +1099,6 @@ top:3px;
left:3px;
}
.pagination {
float:left;
clear:both;
@ -1155,7 +1144,6 @@ padding-right:30px;
}
/* END: NOTICE */
.hentry .entry-content p {
margin-bottom:18px;
}
@ -1172,7 +1160,6 @@ margin-bottom:18px;
margin-left:18px;
}
/* TOP_POSTERS */
.section tbody td {
padding-right:18px;
@ -1200,7 +1187,6 @@ margin-right:0;
display:none;
}
/* tagcloud */
.tag-cloud {
list-style-type:none;
@ -1317,3 +1303,6 @@ display:none;
.guide {
clear:both;
}
.admin_indicator {
font-style:italic;
}