}
/**
- * connect the configured Jabber account to the configured server
+ * Build an XMPP proxy connection that'll save outgoing messages
+ * to the 'xmppout' queue to be picked up by xmppdaemon later.
+ */
+function jabber_proxy()
+{
+ $proxy = new Queued_XMPP(common_config('xmpp', 'host') ?
+ common_config('xmpp', 'host') :
+ common_config('xmpp', 'server'),
+ common_config('xmpp', 'port'),
+ common_config('xmpp', 'user'),
+ common_config('xmpp', 'password'),
+ common_config('xmpp', 'resource') . 'daemon',
+ common_config('xmpp', 'server'),
+ common_config('xmpp', 'debug') ?
+ true : false,
+ common_config('xmpp', 'debug') ?
+ XMPPHP_Log::LEVEL_VERBOSE : null);
+ return $proxy;
+}
+
+/**
+ * Lazy-connect the configured Jabber account to the configured server;
+ * if already opened, the same connection will be returned.
+ *
+ * In a multi-site background process, each site configuration
+ * will get its own connection.
*
* @param string $resource Resource to connect (defaults to configured resource)
*
function jabber_connect($resource=null)
{
- static $conn = null;
- if (!$conn) {
+ static $connections = array();
+ $site = common_config('site', 'server');
+ if (empty($connections[$site])) {
+ if (empty($resource)) {
+ $resource = common_config('xmpp', 'resource');
+ }
$conn = new Sharing_XMPP(common_config('xmpp', 'host') ?
common_config('xmpp', 'host') :
common_config('xmpp', 'server'),
common_config('xmpp', 'port'),
common_config('xmpp', 'user'),
common_config('xmpp', 'password'),
- ($resource) ? $resource :
- common_config('xmpp', 'resource'),
+ $resource,
common_config('xmpp', 'server'),
common_config('xmpp', 'debug') ?
true : false,
if (!$conn) {
return false;
}
+ $connections[$site] = $conn;
$conn->autoSubscribe();
$conn->useEncryption(common_config('xmpp', 'encryption'));
try {
- $conn->connect(true); // true = persistent connection
+ common_log(LOG_INFO, __METHOD__ . ": connecting " .
+ common_config('xmpp', 'user') . '/' . $resource);
+ //$conn->connect(true); // true = persistent connection
+ $conn->connect(); // persistent connections break multisite
} catch (XMPPHP_Exception $e) {
common_log(LOG_ERR, $e->getMessage());
return false;
$conn->processUntil('session_start');
}
- return $conn;
+ return $connections[$site];
}
/**
- * send a single notice to a given Jabber address
+ * Queue send for a single notice to a given Jabber address
*
* @param string $to JID to send the notice to
* @param Notice $notice notice to send
function jabber_send_notice($to, $notice)
{
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
+ $conn = jabber_proxy();
$profile = Profile::staticGet($notice->profile_id);
if (!$profile) {
common_log(LOG_WARNING, 'Refusing to send notice with ' .
$xs = new XMLStringer();
$xs->elementStart('html', array('xmlns' => 'http://jabber.org/protocol/xhtml-im'));
$xs->elementStart('body', array('xmlns' => 'http://www.w3.org/1999/xhtml'));
- $xs->element("img", array('src'=> $profile->avatarUrl(AVATAR_MINI_SIZE) , 'alt' => $profile->nickname));
$xs->element('a', array('href' => $profile->profileurl),
$profile->nickname);
$xs->text(": ");
} else {
$xs->raw(common_render_content($notice->content, $notice));
}
- $xs->raw(" ");
+ $xs->text(" ");
$xs->element('a', array(
'href'=>common_local_url('conversation',
array('id' => $notice->conversation)).'#notice-'.$notice->id
- ),sprintf(_('notice id: %s'),$notice->id));
+ ),sprintf(_('[%s]'),$notice->id));
$xs->elementEnd('body');
$xs->elementEnd('html');
function jabber_send_message($to, $body, $type='chat', $subject=null)
{
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
+ $conn = jabber_proxy();
$conn->message($to, $body, $type, $subject);
return true;
}
}
/**
- * broadcast a notice to all subscribers and reply recipients
+ * Queue broadcast of a notice to all subscribers and reply recipients
*
* This function will send a notice to all subscribers on the local server
* who have Jabber addresses, and have Jabber notification enabled, and
$sent_to = array();
- $conn = jabber_connect();
+ $conn = jabber_proxy();
- // First, get users to whom this is a direct reply
- $user = new User();
- $UT = common_config('db','type')=='pgsql'?'"user"':'user';
- $user->query("SELECT $UT.id, $UT.jabber " .
- "FROM $UT JOIN reply ON $UT.id = reply.profile_id " .
- 'WHERE reply.notice_id = ' . $notice->id . ' ' .
- "AND $UT.jabber is not null " .
- "AND $UT.jabbernotify = 1 " .
- "AND $UT.jabberreplies = 1 ");
-
- while ($user->fetch()) {
- common_log(LOG_INFO,
- 'Sending reply notice ' . $notice->id . ' to ' . $user->jabber,
- __FILE__);
- $conn->message($user->jabber, $msg, 'chat', null, $entry);
- $conn->processTime(0);
- $sent_to[$user->id] = 1;
- }
-
- $user->free();
-
- // Now, get users subscribed to this profile
-
- $user = new User();
- $user->query("SELECT $UT.id, $UT.jabber " .
- "FROM $UT JOIN subscription " .
- "ON $UT.id = subscription.subscriber " .
- 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
- "AND $UT.jabber is not null " .
- "AND $UT.jabbernotify = 1 " .
- 'AND subscription.jabber = 1 ');
+ $ni = $notice->whoGets();
- while ($user->fetch()) {
- if (!array_key_exists($user->id, $sent_to)) {
- common_log(LOG_INFO,
- 'Sending notice ' . $notice->id . ' to ' . $user->jabber,
- __FILE__);
- $conn->message($user->jabber, $msg, 'chat', null, $entry);
- // To keep the incoming queue from filling up,
- // we service it after each send.
- $conn->processTime(0);
- $sent_to[$user->id] = 1;
+ foreach ($ni as $user_id => $reason) {
+ $user = User::staticGet($user_id);
+ if (empty($user) ||
+ empty($user->jabber) ||
+ !$user->jabbernotify) {
+ // either not a local user, or just not found
+ continue;
}
- }
-
- // Now, get users who have it in their inbox because of groups
-
- $user = new User();
- $user->query("SELECT $UT.id, $UT.jabber " .
- "FROM $UT JOIN notice_inbox " .
- "ON $UT.id = notice_inbox.user_id " .
- 'WHERE notice_inbox.notice_id = ' . $notice->id . ' ' .
- 'AND notice_inbox.source = 2 ' .
- "AND $UT.jabber is not null " .
- "AND $UT.jabbernotify = 1 ");
-
- while ($user->fetch()) {
- if (!array_key_exists($user->id, $sent_to)) {
- common_log(LOG_INFO,
- 'Sending notice ' . $notice->id . ' to ' . $user->jabber,
- __FILE__);
- $conn->message($user->jabber, $msg, 'chat', null, $entry);
- // To keep the incoming queue from filling up,
- // we service it after each send.
- $conn->processTime(0);
- $sent_to[$user->id] = 1;
+ switch ($reason) {
+ case NOTICE_INBOX_SOURCE_REPLY:
+ if (!$user->jabberreplies) {
+ continue 2;
+ }
+ break;
+ case NOTICE_INBOX_SOURCE_SUB:
+ $sub = Subscription::pkeyGet(array('subscriber' => $user->id,
+ 'subscribed' => $notice->profile_id));
+ if (empty($sub) || !$sub->jabber) {
+ continue 2;
+ }
+ break;
+ case NOTICE_INBOX_SOURCE_GROUP:
+ break;
+ default:
+ throw new Exception(sprintf(_("Unknown inbox source %d."), $reason));
}
- }
- $user->free();
+ common_log(LOG_INFO,
+ 'Sending notice ' . $notice->id . ' to ' . $user->jabber,
+ __FILE__);
+ $conn->message($user->jabber, $msg, 'chat', null, $entry);
+ }
return true;
}
/**
- * send a notice to all public listeners
+ * Queue send of a notice to all public listeners
*
* For notices that are generated on the local system (by users), we can optionally
* forward them to remote listeners by XMPP.
$public = common_config('xmpp', 'public');
- // FIXME PRIV do not send out private messages here
+ // FIXME PRIV don't send out private messages here
// XXX: should we send out non-local messages if public,localonly
// = false? I think not
- if ($public && $notice->is_local) {
+ if ($public && $notice->is_local == Notice::LOCAL_PUBLIC) {
$profile = Profile::staticGet($notice->profile_id);
if (!$profile) {
$msg = jabber_format_notice($profile, $notice);
$entry = jabber_format_entry($profile, $notice);
- $conn = jabber_connect();
+ $conn = jabber_proxy();
foreach ($public as $address) {
common_log(LOG_INFO,
' to public listener ' . $address,
__FILE__);
$conn->message($address, $msg, 'chat', null, $entry);
- $conn->processTime(0);
}
$profile->free();
}
function jabber_format_notice(&$profile, &$notice)
{
- return $profile->nickname . ': ' . $notice->content;
+ return $profile->nickname . ': ' . $notice->content . ' [' . $notice->id . ']';
}