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, systems. We'll probably add another type sometime in the future,
with our own indexing system (maybe like MediaWiki's). 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 Troubleshooting
=============== ===============

View File

@ -67,7 +67,9 @@ class ApiAction extends Action
$this->process_command(); $this->process_command();
} else { } else {
# basic authentication failed # 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(); $this->show_basic_auth_error();
} }
} }

View File

@ -167,6 +167,15 @@ class GroupMemberListItem extends ProfileListItem
$this->group = $group; $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() function showActions()
{ {
$this->startActions(); $this->startActions();

View File

@ -331,6 +331,7 @@ class ShowgroupAction extends GroupDesignAction
{ {
$this->showMembers(); $this->showMembers();
$this->showStatistics(); $this->showStatistics();
$this->showAdmins();
$cloud = new GroupTagCloudSection($this, $this->group); $cloud = new GroupTagCloudSection($this, $this->group);
$cloud->show(); $cloud->show();
} }
@ -369,6 +370,18 @@ class ShowgroupAction extends GroupDesignAction
$this->elementEnd('div'); $this->elementEnd('div');
} }
/**
* Show list of admins
*
* @return void
*/
function showAdmins()
{
$adminSection = new GroupAdminSection($this, $this->group);
$adminSection->show();
}
/** /**
* Show some statistics * Show some statistics
* *
@ -423,3 +436,34 @@ class ShowgroupAction extends GroupDesignAction
$this->elementEnd('div'); $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.'") '; $qry .= '('.$id.', '.$this->id.', '.$source.', "'.$this->created.'") ';
$cnt++; $cnt++;
if ($cnt >= MAX_BOXCARS) { if ($cnt >= MAX_BOXCARS) {
common_debug($qry);
$inbox = new Notice_inbox(); $inbox = new Notice_inbox();
$inbox->query($qry); $inbox->query($qry);
$qry = $qryhdr; $qry = $qryhdr;
@ -883,7 +882,6 @@ class Notice extends Memcached_DataObject
} }
if ($cnt > 0) { if ($cnt > 0) {
common_debug($qry);
$inbox = new Notice_inbox(); $inbox = new Notice_inbox();
$inbox->query($qry); $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 { } else {
$sn = self::memGet('hostname', strtolower($servername)); $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)) { if (!empty($sn)) {

View File

@ -126,6 +126,30 @@ class User_group extends Memcached_DataObject
return $members; 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) function getBlocked($offset=0, $limit=null)
{ {
$qry = $qry =

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

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

View File

@ -524,3 +524,14 @@ create table group_alias (
index group_alias_group_id_idx (group_id) index group_alias_group_id_idx (group_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; ) 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 */ /** XMPPHP_XMLStream */
require_once "XMPP.php"; require_once dirname(__FILE__) . "/XMPP.php";
/** /**
* XMPPHP Main Class * XMPPHP Main Class

View File

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

View File

@ -27,8 +27,8 @@
*/ */
/** XMPPHP_XMLStream */ /** XMPPHP_XMLStream */
require_once "XMLStream.php"; require_once dirname(__FILE__) . "/XMLStream.php";
require_once "Roster.php"; require_once dirname(__FILE__) . "/Roster.php";
/** /**
* XMPPHP Main Class * XMPPHP Main Class
@ -208,6 +208,15 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
$this->send($out); $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 * Message handler

View File

@ -73,13 +73,45 @@ function handleError($error)
exit(-1); 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() function main()
{ {
// quick check for fancy URL auto-detection support in installer. // quick check for fancy URL auto-detection support in installer.
if (isset($_SERVER['REDIRECT_URL']) && ((dirname($_SERVER['REQUEST_URI']) . '/check-fancy') === $_SERVER['REDIRECT_URL'])) { 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."); 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(); Snapshot::check();
@ -146,19 +178,7 @@ function main()
} else { } else {
$action_obj = new $action_class(); $action_obj = new $action_class();
// XXX: find somewhere for this little block to live checkMirror($action_obj);
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;
}
try { try {
if ($action_obj->prepare($args)) { if ($action_obj->prepare($args)) {

View File

@ -254,6 +254,9 @@ $config =
'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'), 'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
'search' => 'search' =>
array('type' => 'fulltext'), 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'); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');

View File

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

View File

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

View File

@ -139,8 +139,23 @@ function common_have_session()
function common_ensure_session() function common_ensure_session()
{ {
$c = null;
if (array_key_exists(session_name, $_COOKIE)) {
$c = $_COOKIE[session_name()];
}
if (!common_have_session()) { if (!common_have_session()) {
if (common_config('sessions', 'handle')) {
common_log(LOG_INFO, "Using our own session handler");
Session::setSaveHandler();
}
@session_start(); @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 // It comes in special'd, so we unspecial it before passing to the stringifying
// functions // functions
$url = htmlspecialchars_decode($url); $url = htmlspecialchars_decode($url);
$display = File_redirection::_canonUrl($url);
$canon = File_redirection::_canonUrl($url);
$longurl_data = File_redirection::where($url); $longurl_data = File_redirection::where($url);
if (is_array($longurl_data)) { if (is_array($longurl_data)) {
$longurl = $longurl_data['url']; $longurl = $longurl_data['url'];
} elseif (is_string($longurl_data)) { } elseif (is_string($longurl_data)) {
$longurl = $longurl_data; $longurl = $longurl_data;
} else { } 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; $is_attachment = false;
$attachment_id = null; $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' // 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. // 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 need a better test telling what can be shown as an attachment
// we're currently picking up oembeds only. // we're currently picking up oembeds only.
// I think the best option is another file_view table in the db // I think the best option is another file_view table in the db
// and associated dbobject. // 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'"; $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; $file = new File;
@ -549,7 +566,7 @@ function common_linkify($url) {
$attrs['id'] = "attachment-{$attachment_id}"; $attrs['id'] = "attachment-{$attachment_id}";
} }
return XMLStringer::estring('a', $attrs, $display); return XMLStringer::estring('a', $attrs, $url);
} }
function common_shorten_links($text) function common_shorten_links($text)
@ -817,7 +834,12 @@ function common_date_iso8601($dt)
function common_sql_now() 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) 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) function common_log($priority, $msg, $filename=null)
{ {
$logfile = common_config('site', 'logfile'); $logfile = common_config('site', 'logfile');
if ($logfile) { if ($logfile) {
$log = fopen($logfile, "a"); $log = fopen($logfile, "a");
if ($log) { if ($log) {
static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR', $output = common_log_line($priority, $msg);
'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
$output = date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n";
fwrite($log, $output); fwrite($log, $output);
fclose($log); fclose($log);
} }
@ -1321,18 +1348,39 @@ function common_canonical_sms($sms)
function common_error_handler($errno, $errstr, $errfile, $errline, $errcontext) function common_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
{ {
switch ($errno) { switch ($errno) {
case E_ERROR:
case E_COMPILE_ERROR:
case E_CORE_ERROR:
case E_USER_ERROR: case E_USER_ERROR:
common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline)"); case E_PARSE:
exit(1); case E_RECOVERABLE_ERROR:
common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline) [ABORT]");
die();
break; break;
case E_WARNING:
case E_COMPILE_WARNING:
case E_CORE_WARNING:
case E_USER_WARNING: case E_USER_WARNING:
common_log(LOG_WARNING, "[$errno] $errstr ($errfile:$errline)"); common_log(LOG_WARNING, "[$errno] $errstr ($errfile:$errline)");
break; break;
case E_NOTICE:
case E_USER_NOTICE: case E_USER_NOTICE:
common_log(LOG_NOTICE, "[$errno] $errstr ($errfile:$errline)"); common_log(LOG_NOTICE, "[$errno] $errstr ($errfile:$errline)");
break; 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 // FIXME: show error page if we're on the Web
@ -1471,3 +1519,27 @@ function common_shorten_url($long_url)
return $short_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(); $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() function show_help()
{ {
global $helptext; global $helptext;
$_default_help_text = <<<END_OF_DEFAULT $_default_help_text = <<<END_OF_DEFAULT
General options: General options:
-q --quiet Quiet (little output) -q --quiet Quiet (little output)
-v --verbose Verbose (lots of output) -v --verbose Verbose (lots of output)
@ -115,24 +122,53 @@ require_once INSTALLDIR . '/lib/common.php';
set_error_handler('common_error_handler'); set_error_handler('common_error_handler');
function have_option($str) function _make_matches($opt, $alt)
{
$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 have_option($opt, $alt=null)
{ {
global $options; global $options;
$matches = _make_matches($opt, $alt);
foreach ($options as $option) { foreach ($options as $option) {
if ($option[0] == $str) { if (in_array($option[0], $matches)) {
return true; return true;
} }
} }
return false; return false;
} }
function get_option_value($str) function get_option_value($opt, $alt=null)
{ {
global $options; global $options;
$matches = _make_matches($opt, $alt);
foreach ($options as $option) { foreach ($options as $option) {
if ($option[0] == $str) { if (in_array($option[0], $matches)) {
return $option[1]; return $option[1];
} }
} }
return null; return null;
} }

View File

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

View File

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

View File

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

View File

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