summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_darcs/inventory4
-rw-r--r--_darcs/patches/20081222173249-84dde-202ba409e32e2b27089a1bc5431507c9d8a2782e.gzbin0 -> 5199 bytes
-rw-r--r--_darcs/pristine/lib/jabber.php655
-rw-r--r--_darcs/tentative_pristine1143
-rw-r--r--lib/jabber.php655
5 files changed, 1444 insertions, 1013 deletions
diff --git a/_darcs/inventory b/_darcs/inventory
index 3fba38822..b3846949e 100644
--- a/_darcs/inventory
+++ b/_darcs/inventory
@@ -29,4 +29,6 @@ Evan Prodromou <evan@prodromou.name>**20081221003955]
[reformat lib/daemon.php for phpcs
Evan Prodromou <evan@prodromou.name>**20081221004607]
[reformat for phpcs
-Evan Prodromou <evan@prodromou.name>**20081221005837] \ No newline at end of file
+Evan Prodromou <evan@prodromou.name>**20081221005837]
+[reformat lib/jabber.php for phpcs, including doc comments
+Evan Prodromou <evan@prodromou.name>**20081222173249] \ No newline at end of file
diff --git a/_darcs/patches/20081222173249-84dde-202ba409e32e2b27089a1bc5431507c9d8a2782e.gz b/_darcs/patches/20081222173249-84dde-202ba409e32e2b27089a1bc5431507c9d8a2782e.gz
new file mode 100644
index 000000000..908bf5769
--- /dev/null
+++ b/_darcs/patches/20081222173249-84dde-202ba409e32e2b27089a1bc5431507c9d8a2782e.gz
Binary files differ
diff --git a/_darcs/pristine/lib/jabber.php b/_darcs/pristine/lib/jabber.php
index ab0fd6af8..099ded9eb 100644
--- a/_darcs/pristine/lib/jabber.php
+++ b/_darcs/pristine/lib/jabber.php
@@ -1,9 +1,12 @@
<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+/**
+ * Laconica, the distributed open-source microblogging tool
*
- * This program is free software: you can redistribute it and/or modify
+ * utility functions for Jabber/GTalk/XMPP messages
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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.
@@ -15,286 +18,448 @@
*
* 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/>.
+ *
+ * @category Network
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2008 Control Yourself, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
*/
-if (!defined('LACONICA')) { exit(1); }
+if (!defined('LACONICA')) {
+ exit(1);
+}
-require_once('XMPPHP/XMPP.php');
+require_once 'XMPPHP/XMPP.php';
-function jabber_valid_base_jid($jid) {
- # Cheap but effective
- return Validate::email($jid);
-}
+/**
+ * checks whether a string is a syntactically valid Jabber ID (JID)
+ *
+ * @param string $jid string to check
+ *
+ * @return boolean whether the string is a valid JID
+ */
-function jabber_normalize_jid($jid) {
- if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) {
- $node = $matches[1];
- $server = $matches[2];
- return strtolower($node.'@'.$server);
- } else {
- return NULL;
- }
+function jabber_valid_base_jid($jid)
+{
+ // Cheap but effective
+ return Validate::email($jid);
}
-function jabber_daemon_address() {
- return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
+/**
+ * normalizes a Jabber ID for comparison
+ *
+ * @param string $jid JID to check
+ *
+ * @return string an equivalent JID in normalized (lowercase) form
+ */
+
+function jabber_normalize_jid($jid)
+{
+ if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) {
+ $node = $matches[1];
+ $server = $matches[2];
+ return strtolower($node.'@'.$server);
+ } else {
+ return null;
+ }
}
-function jabber_connect($resource=NULL) {
- static $conn = NULL;
- if (!$conn) {
- $conn = new XMPPHP_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'),
- common_config('xmpp', 'server'),
- common_config('xmpp', 'debug') ?
- true : false,
- common_config('xmpp', 'debug') ?
- XMPPHP_Log::LEVEL_VERBOSE : NULL
- );
-
- if (!$conn) {
- return false;
- }
-
- $conn->autoSubscribe();
- $conn->useEncryption(common_config('xmpp', 'encryption'));
-
- try {
- $conn->connect(true); # true = persistent connection
- } catch (XMPPHP_Exception $e) {
- common_log(LOG_ERROR, $e->getMessage());
- return false;
- }
-
- $conn->processUntil('session_start');
- }
- return $conn;
+/**
+ * the JID of the Jabber daemon for this Laconica instance
+ *
+ * @return string JID of the Jabber daemon
+ */
+
+function jabber_daemon_address()
+{
+ return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
}
-function jabber_send_notice($to, $notice) {
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
- $profile = Profile::staticGet($notice->profile_id);
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to send notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return false;
- }
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
- $conn->message($to, $msg, 'chat', NULL, $entry);
- $profile->free();
- return true;
+/**
+ * connect the configured Jabber account to the configured server
+ *
+ * @param string $resource Resource to connect (defaults to configured resource)
+ *
+ * @return XMPPHP connection to the configured server
+ */
+
+function jabber_connect($resource=null)
+{
+ static $conn = null;
+ if (!$conn) {
+ $conn = new XMPPHP_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'),
+ common_config('xmpp', 'server'),
+ common_config('xmpp', 'debug') ?
+ true : false,
+ common_config('xmpp', 'debug') ?
+ XMPPHP_Log::LEVEL_VERBOSE : null
+ );
+
+ if (!$conn) {
+ return false;
+ }
+
+ $conn->autoSubscribe();
+ $conn->useEncryption(common_config('xmpp', 'encryption'));
+
+ try {
+ $conn->connect(true); // true = persistent connection
+ } catch (XMPPHP_Exception $e) {
+ common_log(LOG_ERROR, $e->getMessage());
+ return false;
+ }
+
+ $conn->processUntil('session_start');
+ }
+ return $conn;
}
-# Extra stuff defined by Twitter, needed by twitter clients
-
-function jabber_format_entry($profile, $notice) {
-
- # FIXME: notice url might be remote
-
- $noticeurl = common_local_url('shownotice',
- array('notice' => $notice->id));
- $msg = jabber_format_notice($profile, $notice);
- $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n";
- $entry .= "<source>\n";
- $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n";
- $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n";
- $entry .= "<link rel='self' type='application/rss+xml' href='" . common_local_url('userrss', array('nickname' => $profile->nickname)) . "'/>\n";
- $entry .= "<author><name>" . $profile->nickname . "</name></author>\n";
- $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n";
- $entry .= "</source>\n";
- $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n";
- $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n";
- $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n";
- $entry .= "<id>". $notice->uri . "</id>\n";
- $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n";
- $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n";
- $entry .= "</entry>\n";
-
- $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n";
- $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n";
- $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: ";
- $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice);
- $html .= "\n</body>\n";
- $html .= "\n</html>\n";
-
- $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n";
- $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n";
- $address .= "</addresses>\n";
-
- # FIXME: include a pubsub event, too.
-
- return $html . $entry . $address;
+/**
+ * send a single notice to a given Jabber address
+ *
+ * @param string $to JID to send the notice to
+ * @param Notice $notice notice to send
+ *
+ * @return boolean success value
+ */
+
+function jabber_send_notice($to, $notice)
+{
+ $conn = jabber_connect();
+ if (!$conn) {
+ return false;
+ }
+ $profile = Profile::staticGet($notice->profile_id);
+ if (!$profile) {
+ common_log(LOG_WARNING, 'Refusing to send notice with ' .
+ 'unknown profile ' . common_log_objstring($notice),
+ __FILE__);
+ return false;
+ }
+ $msg = jabber_format_notice($profile, $notice);
+ $entry = jabber_format_entry($profile, $notice);
+ $conn->message($to, $msg, 'chat', null, $entry);
+ $profile->free();
+ return true;
}
-function jabber_send_message($to, $body, $type='chat', $subject=NULL) {
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
- $conn->message($to, $body, $type, $subject);
- return true;
+/**
+ * extra information for XMPP messages, as defined by Twitter
+ *
+ * @param Profile $profile Profile of the sending user
+ * @param Notice $notice Notice being sent
+ *
+ * @return string Extra information (Atom, HTML, addresses) in string format
+ */
+
+function jabber_format_entry($profile, $notice)
+{
+ // FIXME: notice url might be remote
+
+ $noticeurl = common_local_url('shownotice',
+ array('notice' => $notice->id));
+
+ $msg = jabber_format_notice($profile, $notice);
+
+ $self_url = common_local_url('userrss', array('nickname' => $profile->nickname));
+
+ $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n";
+ $entry .= "<source>\n";
+ $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n";
+ $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n";
+ $entry .= "<link rel='self' type='application/rss+xml' href='" . $self_url . "'/>\n";
+ $entry .= "<author><name>" . $profile->nickname . "</name></author>\n";
+ $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n";
+ $entry .= "</source>\n";
+ $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n";
+ $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n";
+ $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n";
+ $entry .= "<id>". $notice->uri . "</id>\n";
+ $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n";
+ $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n";
+ $entry .= "</entry>\n";
+
+ $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n";
+ $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n";
+ $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: ";
+ $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice);
+ $html .= "\n</body>\n";
+ $html .= "\n</html>\n";
+
+ $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n";
+ $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n";
+ $address .= "</addresses>\n";
+
+ // FIXME: include a pubsub event, too.
+
+ return $html . $entry . $address;
}
-function jabber_send_presence($status, $show='available', $to=NULL,
- $type = 'available', $priority=NULL)
+/**
+ * sends a single text message to a given JID
+ *
+ * @param string $to JID to send the message to
+ * @param string $body body of the message
+ * @param string $type type of the message
+ * @param string $subject subject of the message
+ *
+ * @return boolean success flag
+ */
+
+function jabber_send_message($to, $body, $type='chat', $subject=null)
{
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
- $conn->presence($status, $show, $to, $type, $priority);
- return true;
+ $conn = jabber_connect();
+ if (!$conn) {
+ return false;
+ }
+ $conn->message($to, $body, $type, $subject);
+ return true;
}
-function jabber_confirm_address($code, $nickname, $address) {
- $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' .
- 'has said that your Jabber ID belongs to them. ' .
- 'If that\'s true, you can confirm by clicking on this URL: ' .
- common_local_url('confirmaddress', array('code' => $code)) .
- ' . (If you cannot click it, copy-and-paste it into the ' .
- 'address bar of your browser). If that user isn\'t you, ' .
- 'or if you didn\'t request this confirmation, just ignore this message.';
+/**
+ * sends a presence stanza on the Jabber network
+ *
+ * @param string $status current status, free-form string
+ * @param string $show structured status value
+ * @param string $to recipient of presence, null for general
+ * @param string $type type of status message, related to $show
+ * @param int $priority priority of the presence
+ *
+ * @return boolean success value
+ */
- return jabber_send_message($address, $body);
+function jabber_send_presence($status, $show='available', $to=null,
+ $type = 'available', $priority=null)
+{
+ $conn = jabber_connect();
+ if (!$conn) {
+ return false;
+ }
+ $conn->presence($status, $show, $to, $type, $priority);
+ return true;
}
-function jabber_special_presence($type, $to=NULL, $show=NULL, $status=NULL) {
- $conn = jabber_connect();
-
- $to = htmlspecialchars($to);
- $status = htmlspecialchars($status);
- $out = "<presence";
- if($to) $out .= " to='$to'";
- if($type) $out .= " type='$type'";
- if($show == 'available' and !$status) {
- $out .= "/>";
- } else {
- $out .= ">";
- if($show && ($show != 'available')) $out .= "<show>$show</show>";
- if($status) $out .= "<status>$status</status>";
- $out .= "</presence>";
- }
- $conn->send($out);
+/**
+ * sends a confirmation request to a JID
+ *
+ * @param string $code confirmation code for confirmation URL
+ * @param string $nickname nickname of confirming user
+ * @param string $address JID to send confirmation to
+ *
+ * @return boolean success flag
+ */
+
+function jabber_confirm_address($code, $nickname, $address)
+{
+ $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' .
+ 'has said that your Jabber ID belongs to them. ' .
+ 'If that\'s true, you can confirm by clicking on this URL: ' .
+ common_local_url('confirmaddress', array('code' => $code)) .
+ ' . (If you cannot click it, copy-and-paste it into the ' .
+ 'address bar of your browser). If that user isn\'t you, ' .
+ 'or if you didn\'t request this confirmation, just ignore this message.';
+
+ return jabber_send_message($address, $body);
}
-function jabber_broadcast_notice($notice) {
-
- if (!common_config('xmpp', 'enabled')) {
- return true;
- }
- $profile = Profile::staticGet($notice->profile_id);
-
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return false;
- }
-
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
-
- $profile->free();
- unset($profile);
-
- $sent_to = array();
- $conn = jabber_connect();
-
- # First, get users to whom this is a direct reply
- $user = new User();
- $user->query('SELECT user.id, user.jabber ' .
- 'FROM user JOIN reply ON user.id = reply.profile_id ' .
- 'WHERE reply.notice_id = ' . $notice->id . ' ' .
- 'AND user.jabber is not null ' .
- 'AND user.jabbernotify = 1 ' .
- 'AND user.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 user.id, user.jabber ' .
- 'FROM user JOIN subscription ON user.id = subscription.subscriber ' .
- 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
- 'AND user.jabber is not null ' .
- 'AND user.jabbernotify = 1 ' .
- 'AND subscription.jabber = 1 ');
+/**
+ * sends a "special" presence stanza on the Jabber network
+ *
+ * @param string $type Type of presence
+ * @param string $to JID to send presence to
+ * @param string $show show value for presence
+ * @param string $status status value for presence
+ *
+ * @return boolean success flag
+ *
+ * @see jabber_send_presence()
+ */
- 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);
- }
- }
+function jabber_special_presence($type, $to=null, $show=null, $status=null)
+{
+ // FIXME: why use this instead of jabber_send_presence()?
+ $conn = jabber_connect();
+
+ $to = htmlspecialchars($to);
+ $status = htmlspecialchars($status);
+
+ $out = "<presence";
+ if ($to) {
+ $out .= " to='$to'";
+ }
+ if ($type) {
+ $out .= " type='$type'";
+ }
+ if ($show == 'available' and !$status) {
+ $out .= "/>";
+ } else {
+ $out .= ">";
+ if ($show && ($show != 'available')) {
+ $out .= "<show>$show</show>";
+ }
+ if ($status) {
+ $out .= "<status>$status</status>";
+ }
+ $out .= "</presence>";
+ }
+ $conn->send($out);
+}
- $user->free();
+/**
+ * broadcast 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
+ * have this subscription enabled for Jabber. It also sends the notice to
+ * all recipients of @-replies who have Jabber addresses and Jabber notification
+ * enabled. This is really the heart of Jabber distribution in Laconica.
+ *
+ * @param Notice $notice The notice to broadcast
+ *
+ * @return boolean success flag
+ */
- return true;
+function jabber_broadcast_notice($notice)
+{
+ if (!common_config('xmpp', 'enabled')) {
+ return true;
+ }
+ $profile = Profile::staticGet($notice->profile_id);
+
+ if (!$profile) {
+ common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
+ 'unknown profile ' . common_log_objstring($notice),
+ __FILE__);
+ return false;
+ }
+
+ $msg = jabber_format_notice($profile, $notice);
+ $entry = jabber_format_entry($profile, $notice);
+
+ $profile->free();
+ unset($profile);
+
+ $sent_to = array();
+
+ $conn = jabber_connect();
+
+ // First, get users to whom this is a direct reply
+ $user = new User();
+ $user->query('SELECT user.id, user.jabber ' .
+ 'FROM user JOIN reply ON user.id = reply.profile_id ' .
+ 'WHERE reply.notice_id = ' . $notice->id . ' ' .
+ 'AND user.jabber is not null ' .
+ 'AND user.jabbernotify = 1 ' .
+ 'AND user.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 user.id, user.jabber ' .
+ 'FROM user JOIN subscription ' .
+ 'ON user.id = subscription.subscriber ' .
+ 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
+ 'AND user.jabber is not null ' .
+ 'AND user.jabbernotify = 1 ' .
+ 'AND subscription.jabber = 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);
+ }
+ }
+
+ $user->free();
+
+ return true;
}
-function jabber_public_notice($notice) {
+/**
+ * send 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.
+ *
+ * @param Notice $notice notice to broadcast
+ *
+ * @return boolean success flag
+ */
- # Now, users who want everything
+function jabber_public_notice($notice)
+{
+ // Now, users who want everything
- $public = common_config('xmpp', 'public');
+ $public = common_config('xmpp', 'public');
- # FIXME PRIV don't send out private messages here
- # XXX: should we send out non-local messages if public,localonly
- # = false? I think not
+ // 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) {
- $profile = Profile::staticGet($notice->profile_id);
+ if ($public && $notice->is_local) {
+ $profile = Profile::staticGet($notice->profile_id);
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return false;
- }
+ if (!$profile) {
+ common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
+ 'unknown profile ' . common_log_objstring($notice),
+ __FILE__);
+ return false;
+ }
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
+ $msg = jabber_format_notice($profile, $notice);
+ $entry = jabber_format_entry($profile, $notice);
- $conn = jabber_connect();
+ $conn = jabber_connect();
- foreach ($public as $address) {
- common_log(LOG_INFO,
- 'Sending notice ' . $notice->id . ' to public listener ' . $address,
- __FILE__);
- $conn->message($address, $msg, 'chat', NULL, $entry);
- $conn->processTime(0);
- }
- $profile->free();
- }
+ foreach ($public as $address) {
+ common_log(LOG_INFO,
+ 'Sending notice ' . $notice->id .
+ ' to public listener ' . $address,
+ __FILE__);
+ $conn->message($address, $msg, 'chat', null, $entry);
+ $conn->processTime(0);
+ }
+ $profile->free();
+ }
- return true;
+ return true;
}
-function jabber_format_notice(&$profile, &$notice) {
- return $profile->nickname . ': ' . $notice->content;
+/**
+ * makes a plain-text formatted version of a notice, suitable for Jabber distribution
+ *
+ * @param Profile &$profile profile of the sending user
+ * @param Notice &$notice notice being sent
+ *
+ * @return string plain-text version of the notice, with user nickname prefixed
+ */
+
+function jabber_format_notice(&$profile, &$notice)
+{
+ return $profile->nickname . ': ' . $notice->content;
}
diff --git a/_darcs/tentative_pristine b/_darcs/tentative_pristine
index 4e6b1fca9..402ea252b 100644
--- a/_darcs/tentative_pristine
+++ b/_darcs/tentative_pristine
@@ -1,562 +1,661 @@
-hunk ./lib/gallery.php 2
--
+hunk ./lib/jabber.php 2
-/*
+- * Laconica - a distributed open-source microblogging tool
+- * Copyright (C) 2008, Controlez-Vous, Inc.
+/**
-hunk ./lib/gallery.php 20
++ * Laconica, the distributed open-source microblogging tool
+hunk ./lib/jabber.php 5
+- * This program is free software: you can redistribute it and/or modify
++ * utility functions for Jabber/GTalk/XMPP messages
++ *
++ * PHP version 5
++ *
++ * LICENCE: This program is free software: you can redistribute it and/or modify
+hunk ./lib/jabber.php 21
++ *
++ * @category Network
++ * @package Laconica
++ * @author Evan Prodromou <evan@controlyourself.ca>
++ * @copyright 2008 Control Yourself, Inc.
++ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
++ * @link http://laconi.ca/
+hunk ./lib/jabber.php 30
-if (!defined('LACONICA')) { exit(1); }
+if (!defined('LACONICA')) {
+ exit(1);
+}
-hunk ./lib/gallery.php 24
--require_once(INSTALLDIR.'/lib/profilelist.php');
-+require_once INSTALLDIR.'/lib/profilelist.php';
-hunk ./lib/gallery.php 26
--# 10x8
-+// 10x8
-hunk ./lib/gallery.php 30
--class GalleryAction extends Action {
-+class GalleryAction extends Action
-+{
-+ function is_readonly()
-+ {
-+ return true;
-+ }
+hunk ./lib/jabber.php 34
+-require_once('XMPPHP/XMPP.php');
++require_once 'XMPPHP/XMPP.php';
+hunk ./lib/jabber.php 36
+-function jabber_valid_base_jid($jid) {
+- # Cheap but effective
+- return Validate::email($jid);
++/**
++ * checks whether a string is a syntactically valid Jabber ID (JID)
++ *
++ * @param string $jid string to check
++ *
++ * @return boolean whether the string is a valid JID
++ */
+
-+ function handle($args)
-+ {
-+ parent::handle($args);
++function jabber_valid_base_jid($jid)
++{
++ // Cheap but effective
++ return Validate::email($jid);
+hunk ./lib/jabber.php 50
+-function jabber_normalize_jid($jid) {
+- if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) {
+- $node = $matches[1];
+- $server = $matches[2];
+- return strtolower($node.'@'.$server);
+- } else {
+- return NULL;
+- }
++/**
++ * normalizes a Jabber ID for comparison
++ *
++ * @param string $jid JID to check
++ *
++ * @return string an equivalent JID in normalized (lowercase) form
++ */
+
-+ // Post from the tag dropdown; redirect to a GET
++function jabber_normalize_jid($jid)
++{
++ if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) {
++ $node = $matches[1];
++ $server = $matches[2];
++ return strtolower($node.'@'.$server);
++ } else {
++ return null;
++ }
+hunk ./lib/jabber.php 69
+-function jabber_daemon_address() {
+- return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
++/**
++ * the JID of the Jabber daemon for this Laconica instance
++ *
++ * @return string JID of the Jabber daemon
++ */
+
-+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-+ common_redirect($this->self_url(), 307);
-+ }
++function jabber_daemon_address()
++{
++ return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
+hunk ./lib/jabber.php 80
+-function jabber_connect($resource=NULL) {
+- static $conn = NULL;
+- if (!$conn) {
+- $conn = new XMPPHP_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'),
+- common_config('xmpp', 'server'),
+- common_config('xmpp', 'debug') ?
+- true : false,
+- common_config('xmpp', 'debug') ?
+- XMPPHP_Log::LEVEL_VERBOSE : NULL
+- );
++/**
++ * connect the configured Jabber account to the configured server
++ *
++ * @param string $resource Resource to connect (defaults to configured resource)
++ *
++ * @return XMPPHP connection to the configured server
++ */
+
-+ $nickname = common_canonical_nickname($this->arg('nickname'));
-hunk ./lib/gallery.php 49
-- function is_readonly() {
-- return true;
-- }
-+ $user = User::staticGet('nickname', $nickname);
-hunk ./lib/gallery.php 51
-- function handle($args) {
-- parent::handle($args);
-+ if (!$user) {
-+ $this->no_such_user();
-+ return;
-+ }
-hunk ./lib/gallery.php 56
-- # Post from the tag dropdown; redirect to a GET
-+ $profile = $user->getProfile();
-hunk ./lib/gallery.php 58
-- if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-- common_redirect($this->self_url(), 307);
-- }
-+ if (!$profile) {
-+ $this->server_error(_('User without matching profile in system.'));
-+ return;
-+ }
-hunk ./lib/gallery.php 63
-- $nickname = common_canonical_nickname($this->arg('nickname'));
-- $user = User::staticGet('nickname', $nickname);
-+ $page = $this->arg('page');
-hunk ./lib/gallery.php 65
-- if (!$user) {
-- $this->no_such_user();
-- return;
++function jabber_connect($resource=null)
++{
++ static $conn = null;
++ if (!$conn) {
++ $conn = new XMPPHP_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'),
++ common_config('xmpp', 'server'),
++ common_config('xmpp', 'debug') ?
++ true : false,
++ common_config('xmpp', 'debug') ?
++ XMPPHP_Log::LEVEL_VERBOSE : null
++ );
+hunk ./lib/jabber.php 107
+- if (!$conn) {
+- return false;
- }
-+ if (!$page) {
-+ $page = 1;
++ if (!$conn) {
++ return false;
+ }
-hunk ./lib/gallery.php 69
-- $profile = $user->getProfile();
-+ $display = $this->arg('display');
-hunk ./lib/gallery.php 71
-- if (!$profile) {
-- $this->server_error(_('User without matching profile in system.'));
-- return;
+hunk ./lib/jabber.php 111
+- $conn->autoSubscribe();
+- $conn->useEncryption(common_config('xmpp', 'encryption'));
++ $conn->autoSubscribe();
++ $conn->useEncryption(common_config('xmpp', 'encryption'));
+hunk ./lib/jabber.php 114
+- try {
+- $conn->connect(true); # true = persistent connection
+- } catch (XMPPHP_Exception $e) {
+- common_log(LOG_ERROR, $e->getMessage());
+- return false;
- }
-+ if (!$display) {
-+ $display = 'list';
++ try {
++ $conn->connect(true); // true = persistent connection
++ } catch (XMPPHP_Exception $e) {
++ common_log(LOG_ERROR, $e->getMessage());
++ return false;
+ }
-hunk ./lib/gallery.php 75
-- $page = $this->arg('page');
-+ $tag = $this->arg('tag');
-hunk ./lib/gallery.php 77
-- if (!$page) {
-- $page = 1;
-- }
-+ common_show_header($profile->nickname . ": " . $this->gallery_type(),
-+ null, $profile,
-+ array($this, 'show_top'));
-hunk ./lib/gallery.php 81
-- $display = $this->arg('display');
-+ $this->display_links($profile, $page, $display);
-+ $this->show_tags_dropdown($profile);
-hunk ./lib/gallery.php 84
-- if (!$display) {
-- $display = 'list';
-- }
-+ $this->show_gallery($profile, $page, $display, $tag);
-+ common_show_footer();
-+ }
-hunk ./lib/gallery.php 88
-- $tag = $this->arg('tag');
-+ function no_such_user()
-+ {
-+ $this->client_error(_('No such user.'));
+hunk ./lib/jabber.php 121
+- $conn->processUntil('session_start');
+- }
+- return $conn;
++ $conn->processUntil('session_start');
+ }
-hunk ./lib/gallery.php 93
-- common_show_header($profile->nickname . ": " . $this->gallery_type(),
-- NULL, $profile,
-- array($this, 'show_top'));
-+ function show_tags_dropdown($profile)
-+ {
-+ $tag = $this->trimmed('tag');
-hunk ./lib/gallery.php 97
-- $this->display_links($profile, $page, $display);
-- $this->show_tags_dropdown($profile);
-+ list($lst, $usr) = $this->fields();
-hunk ./lib/gallery.php 99
-- $this->show_gallery($profile, $page, $display, $tag);
-- common_show_footer();
++ return $conn;
+hunk ./lib/jabber.php 126
+-function jabber_send_notice($to, $notice) {
+- $conn = jabber_connect();
+- if (!$conn) {
+- return false;
- }
-+ $tags = $this->get_all_tags($profile, $lst, $usr);
-hunk ./lib/gallery.php 101
-- function no_such_user() {
-- $this->client_error(_('No such user.'));
+- $profile = Profile::staticGet($notice->profile_id);
+- if (!$profile) {
+- common_log(LOG_WARNING, 'Refusing to send notice with ' .
+- 'unknown profile ' . common_log_objstring($notice),
+- __FILE__);
+- return false;
- }
-+ $content = array();
-hunk ./lib/gallery.php 103
-- function show_tags_dropdown($profile) {
-- $tag = $this->trimmed('tag');
-- list($lst, $usr) = $this->fields();
-- $tags = $this->get_all_tags($profile, $lst, $usr);
-- $content = array();
-- foreach ($tags as $t) {
-- $content[$t] = $t;
-- }
-- if ($tags) {
-- common_element_start('dl', array('id'=>'filter_tags'));
-- common_element('dt', null, _('Filter tags'));
-- common_element_start('dd');
-- common_element_start('ul');
-- common_element_start('li', array('id'=>'filter_tags_all', 'class'=>'child_1'));
-- common_element('a', array('href' => common_local_url($this->trimmed('action'),
-- array('nickname' => $profile->nickname))),
-- _('All'));
-- common_element_end('li');
-- common_element_start('li', array('id'=>'filter_tags_item'));
-- common_element_start('form', array('name' => 'bytag', 'id' => 'bytag', 'method' => 'post'));
-- common_dropdown('tag', _('Tag'), $content,
-- _('Choose a tag to narrow list'), FALSE, $tag);
-- common_submit('go', _('Go'));
-- common_element_end('form');
-- common_element_end('li');
-- common_element_end('ul');
-- common_element_end('dd');
-- common_element_end('dl');
-- }
+- $msg = jabber_format_notice($profile, $notice);
+- $entry = jabber_format_entry($profile, $notice);
+- $conn->message($to, $msg, 'chat', NULL, $entry);
+- $profile->free();
+- return true;
++/**
++ * send a single notice to a given Jabber address
++ *
++ * @param string $to JID to send the notice to
++ * @param Notice $notice notice to send
++ *
++ * @return boolean success value
++ */
++
++function jabber_send_notice($to, $notice)
++{
++ $conn = jabber_connect();
++ if (!$conn) {
++ return false;
++ }
++ $profile = Profile::staticGet($notice->profile_id);
++ if (!$profile) {
++ common_log(LOG_WARNING, 'Refusing to send notice with ' .
++ 'unknown profile ' . common_log_objstring($notice),
++ __FILE__);
++ return false;
++ }
++ $msg = jabber_format_notice($profile, $notice);
++ $entry = jabber_format_entry($profile, $notice);
++ $conn->message($to, $msg, 'chat', null, $entry);
++ $profile->free();
++ return true;
+hunk ./lib/jabber.php 155
+-# Extra stuff defined by Twitter, needed by twitter clients
++/**
++ * extra information for XMPP messages, as defined by Twitter
++ *
++ * @param Profile $profile Profile of the sending user
++ * @param Notice $notice Notice being sent
++ *
++ * @return string Extra information (Atom, HTML, addresses) in string format
++ */
+hunk ./lib/jabber.php 164
+-function jabber_format_entry($profile, $notice) {
++function jabber_format_entry($profile, $notice)
++{
++ // FIXME: notice url might be remote
+hunk ./lib/jabber.php 168
+- # FIXME: notice url might be remote
++ $noticeurl = common_local_url('shownotice',
++ array('notice' => $notice->id));
+hunk ./lib/jabber.php 171
+- $noticeurl = common_local_url('shownotice',
+- array('notice' => $notice->id));
+- $msg = jabber_format_notice($profile, $notice);
+- $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n";
+- $entry .= "<source>\n";
+- $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n";
+- $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n";
+- $entry .= "<link rel='self' type='application/rss+xml' href='" . common_local_url('userrss', array('nickname' => $profile->nickname)) . "'/>\n";
+- $entry .= "<author><name>" . $profile->nickname . "</name></author>\n";
+- $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n";
+- $entry .= "</source>\n";
+- $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n";
+- $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n";
+- $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n";
+- $entry .= "<id>". $notice->uri . "</id>\n";
+- $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n";
+- $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n";
+- $entry .= "</entry>\n";
++ $msg = jabber_format_notice($profile, $notice);
+hunk ./lib/jabber.php 173
+- $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n";
+- $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n";
+- $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: ";
+- $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice);
+- $html .= "\n</body>\n";
+- $html .= "\n</html>\n";
++ $self_url = common_local_url('userrss', array('nickname' => $profile->nickname));
+hunk ./lib/jabber.php 175
+- $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n";
+- $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n";
+- $address .= "</addresses>\n";
++ $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n";
++ $entry .= "<source>\n";
++ $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n";
++ $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n";
++ $entry .= "<link rel='self' type='application/rss+xml' href='" . $self_url . "'/>\n";
++ $entry .= "<author><name>" . $profile->nickname . "</name></author>\n";
++ $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n";
++ $entry .= "</source>\n";
++ $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n";
++ $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n";
++ $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n";
++ $entry .= "<id>". $notice->uri . "</id>\n";
++ $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n";
++ $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n";
++ $entry .= "</entry>\n";
+hunk ./lib/jabber.php 191
+- # FIXME: include a pubsub event, too.
++ $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n";
++ $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n";
++ $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: ";
++ $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice);
++ $html .= "\n</body>\n";
++ $html .= "\n</html>\n";
+hunk ./lib/jabber.php 198
+- return $html . $entry . $address;
++ $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n";
++ $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n";
++ $address .= "</addresses>\n";
++
++ // FIXME: include a pubsub event, too.
++
++ return $html . $entry . $address;
+hunk ./lib/jabber.php 207
+-function jabber_send_message($to, $body, $type='chat', $subject=NULL) {
+- $conn = jabber_connect();
+- if (!$conn) {
+- return false;
- }
-+ foreach ($tags as $t) {
-+ $content[$t] = $t;
-+ }
-+ if ($tags) {
-+ common_element_start('dl', array('id'=>'filter_tags'));
-+ common_element('dt', null, _('Filter tags'));
-+ common_element_start('dd');
-+ common_element_start('ul');
-+ common_element_start('li', array('id' => 'filter_tags_all',
-+ 'class' => 'child_1'));
-+ common_element('a',
-+ array('href' =>
-+ common_local_url($this->trimmed('action'),
-+ array('nickname' =>
-+ $profile->nickname))),
-+ _('All'));
-+ common_element_end('li');
-+ common_element_start('li', array('id'=>'filter_tags_item'));
-+ common_element_start('form', array('name' => 'bytag',
-+ 'id' => 'bytag',
-+ 'method' => 'post'));
-+ common_dropdown('tag', _('Tag'), $content,
-+ _('Choose a tag to narrow list'), false, $tag);
-+ common_submit('go', _('Go'));
-+ common_element_end('form');
-+ common_element_end('li');
-+ common_element_end('ul');
-+ common_element_end('dd');
-+ common_element_end('dl');
-+ }
+- $conn->message($to, $body, $type, $subject);
+- return true;
++/**
++ * sends a single text message to a given JID
++ *
++ * @param string $to JID to send the message to
++ * @param string $body body of the message
++ * @param string $type type of the message
++ * @param string $subject subject of the message
++ *
++ * @return boolean success flag
++ */
++
++function jabber_send_message($to, $body, $type='chat', $subject=null)
++{
++ $conn = jabber_connect();
++ if (!$conn) {
++ return false;
+ }
-hunk ./lib/gallery.php 135
-- function show_top($profile) {
-- common_element('div', 'instructions',
-- $this->get_instructions($profile));
-+ function show_top($profile)
-+ {
-+ common_element('div', 'instructions',
-+ $this->get_instructions($profile));
-hunk ./lib/gallery.php 140
++ $conn->message($to, $body, $type, $subject);
++ return true;
+hunk ./lib/jabber.php 228
+-function jabber_send_presence($status, $show='available', $to=NULL,
+- $type = 'available', $priority=NULL)
++/**
++ * sends a presence stanza on the Jabber network
++ *
++ * @param string $status current status, free-form string
++ * @param string $show structured status value
++ * @param string $to recipient of presence, null for general
++ * @param string $type type of status message, related to $show
++ * @param int $priority priority of the presence
++ *
++ * @return boolean success value
++ */
++
++function jabber_send_presence($status, $show='available', $to=null,
++ $type = 'available', $priority=null)
+hunk ./lib/jabber.php 243
+- $conn = jabber_connect();
+- if (!$conn) {
+- return false;
- }
+- $conn->presence($status, $show, $to, $type, $priority);
+- return true;
++ $conn = jabber_connect();
++ if (!$conn) {
++ return false;
+ }
-hunk ./lib/gallery.php 142
-- function show_menu() {
-- # action => array('prompt', 'title', $args)
-- $action = $this->trimmed('action');
-- $nickname = $this->trimmed('nickname');
-- $menu =
-- array('subscriptions' =>
-- array( _('Subscriptions'),
-- _('Subscriptions'),
-- array('nickname' => $nickname)),
-- 'subscribers' =>
-- array(
-- _('Subscribers'),
-- _('Subscribers'),
-- array('nickname' => $nickname)),
-- );
-- $this->nav_menu($menu);
++ $conn->presence($status, $show, $to, $type, $priority);
++ return true;
+hunk ./lib/jabber.php 251
+-function jabber_confirm_address($code, $nickname, $address) {
+- $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' .
+- 'has said that your Jabber ID belongs to them. ' .
+- 'If that\'s true, you can confirm by clicking on this URL: ' .
+- common_local_url('confirmaddress', array('code' => $code)) .
+- ' . (If you cannot click it, copy-and-paste it into the ' .
+- 'address bar of your browser). If that user isn\'t you, ' .
+- 'or if you didn\'t request this confirmation, just ignore this message.';
++/**
++ * sends a confirmation request to a JID
++ *
++ * @param string $code confirmation code for confirmation URL
++ * @param string $nickname nickname of confirming user
++ * @param string $address JID to send confirmation to
++ *
++ * @return boolean success flag
++ */
++
++function jabber_confirm_address($code, $nickname, $address)
++{
++ $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' .
++ 'has said that your Jabber ID belongs to them. ' .
++ 'If that\'s true, you can confirm by clicking on this URL: ' .
++ common_local_url('confirmaddress', array('code' => $code)) .
++ ' . (If you cannot click it, copy-and-paste it into the ' .
++ 'address bar of your browser). If that user isn\'t you, ' .
++ 'or if you didn\'t request this confirmation, just ignore this message.';
+hunk ./lib/jabber.php 271
+- return jabber_send_message($address, $body);
++ return jabber_send_message($address, $body);
+hunk ./lib/jabber.php 274
+-function jabber_special_presence($type, $to=NULL, $show=NULL, $status=NULL) {
+- $conn = jabber_connect();
++/**
++ * sends a "special" presence stanza on the Jabber network
++ *
++ * @param string $type Type of presence
++ * @param string $to JID to send presence to
++ * @param string $show show value for presence
++ * @param string $status status value for presence
++ *
++ * @return boolean success flag
++ *
++ * @see jabber_send_presence()
++ */
++
++function jabber_special_presence($type, $to=null, $show=null, $status=null)
++{
++ // FIXME: why use this instead of jabber_send_presence()?
++ $conn = jabber_connect();
+hunk ./lib/jabber.php 292
+- $to = htmlspecialchars($to);
+- $status = htmlspecialchars($status);
+- $out = "<presence";
+- if($to) $out .= " to='$to'";
+- if($type) $out .= " type='$type'";
+- if($show == 'available' and !$status) {
+- $out .= "/>";
+- } else {
+- $out .= ">";
+- if($show && ($show != 'available')) $out .= "<show>$show</show>";
+- if($status) $out .= "<status>$status</status>";
+- $out .= "</presence>";
- }
-+ function show_menu()
-+ {
-+ // action => array('prompt', 'title', $args)
-+ $action = $this->trimmed('action');
-+ $nickname = $this->trimmed('nickname');
-hunk ./lib/gallery.php 148
-- function show_gallery($profile, $page, $display='list', $tag=NULL) {
-+ $menu =
-+ array('subscriptions' =>
-+ array( _('Subscriptions'),
-+ _('Subscriptions'),
-+ array('nickname' => $nickname)),
-+ 'subscribers' =>
-+ array(
-+ _('Subscribers'),
-+ _('Subscribers'),
-+ array('nickname' => $nickname)),
-+ );
-+ $this->nav_menu($menu);
+- $conn->send($out);
++ $to = htmlspecialchars($to);
++ $status = htmlspecialchars($status);
++
++ $out = "<presence";
++ if ($to) {
++ $out .= " to='$to'";
+ }
-hunk ./lib/gallery.php 162
-- $other = new Profile();
-+ function show_gallery($profile, $page, $display='list', $tag=null)
-+ {
-+ $other = new Profile();
-hunk ./lib/gallery.php 166
-- list($lst, $usr) = $this->fields();
-+ list($lst, $usr) = $this->fields();
-hunk ./lib/gallery.php 168
-- $per_page = ($display == 'list') ? PROFILES_PER_PAGE : AVATARS_PER_PAGE;
-+ $per_page = ($display == 'list') ? PROFILES_PER_PAGE : AVATARS_PER_PAGE;
-hunk ./lib/gallery.php 170
-- $offset = ($page-1)*$per_page;
-- $limit = $per_page + 1;
-+ $offset = ($page-1)*$per_page;
-+ $limit = $per_page + 1;
-hunk ./lib/gallery.php 173
-- if (common_config('db','type') == 'pgsql') {
-- $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-- } else {
-- $lim = ' LIMIT ' . $offset . ', ' . $limit;
-- }
-+ if (common_config('db', 'type') == 'pgsql') {
-+ $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-+ } else {
-+ $lim = ' LIMIT ' . $offset . ', ' . $limit;
++ if ($type) {
++ $out .= " type='$type'";
++ }
++ if ($show == 'available' and !$status) {
++ $out .= "/>";
++ } else {
++ $out .= ">";
++ if ($show && ($show != 'available')) {
++ $out .= "<show>$show</show>";
+ }
-hunk ./lib/gallery.php 179
-- # XXX: memcached results
-- # FIXME: SQL injection on $tag
-+ // XXX: memcached results
-+ // FIXME: SQL injection on $tag
-hunk ./lib/gallery.php 182
-- $other->query('SELECT profile.* ' .
-- 'FROM profile JOIN subscription ' .
-- 'ON profile.id = subscription.' . $lst . ' ' .
-- (($tag) ? 'JOIN profile_tag ON (profile.id = profile_tag.tagged AND subscription.'.$usr.'= profile_tag.tagger) ' : '') .
-- 'WHERE ' . $usr . ' = ' . $profile->id . ' ' .
-- 'AND subscriber != subscribed ' .
-- (($tag) ? 'AND profile_tag.tag= "' . $tag . '" ': '') .
-- 'ORDER BY subscription.created DESC, profile.id DESC ' .
-- $lim);
-+ $other->query('SELECT profile.* ' .
-+ 'FROM profile JOIN subscription ' .
-+ 'ON profile.id = subscription.' . $lst . ' ' .
-+ (($tag) ? 'JOIN profile_tag ON (profile.id = profile_tag.tagged AND subscription.'.$usr.'= profile_tag.tagger) ' : '') .
-+ 'WHERE ' . $usr . ' = ' . $profile->id . ' ' .
-+ 'AND subscriber != subscribed ' .
-+ (($tag) ? 'AND profile_tag.tag= "' . $tag . '" ': '') .
-+ 'ORDER BY subscription.created DESC, profile.id DESC ' .
-+ $lim);
-hunk ./lib/gallery.php 192
-- if ($display == 'list') {
-+ if ($display == 'list') {
-hunk ./lib/gallery.php 194
-- $profile_list = new $cls($other, $profile, $this->trimmed('action'));
-- $cnt = $profile_list->show_list();
-- } else {
-- $cnt = $this->icon_list($other);
-- }
-+ $profile_list = new $cls($other, $profile, $this->trimmed('action'));
-+ $cnt = $profile_list->show_list();
-+ } else {
-+ $cnt = $this->icon_list($other);
++ if ($status) {
++ $out .= "<status>$status</status>";
+ }
-hunk ./lib/gallery.php 200
-- # For building the pagination URLs
-+ // For building the pagination URLs
-hunk ./lib/gallery.php 202
-- $args = array('nickname' => $profile->nickname);
-+ $args = array('nickname' => $profile->nickname);
-hunk ./lib/gallery.php 204
-- if ($display != 'list') {
-- $args['display'] = $display;
-- }
-+ if ($display != 'list') {
-+ $args['display'] = $display;
-+ }
-hunk ./lib/gallery.php 208
-- common_pagination($page > 1,
-- $cnt > $per_page,
-- $page,
-- $this->trimmed('action'),
-- $args);
-- }
-+ common_pagination($page > 1,
-+ $cnt > $per_page,
-+ $page,
-+ $this->trimmed('action'),
-+ $args);
++ $out .= "</presence>";
+ }
-hunk ./lib/gallery.php 215
-- function profile_list_class() {
-+ function profile_list_class()
-+ {
-hunk ./lib/gallery.php 220
-- function icon_list($other) {
-+ function icon_list($other)
-+ {
-+ common_element_start('ul', $this->div_class());
-hunk ./lib/gallery.php 224
-- common_element_start('ul', $this->div_class());
-+ $cnt = 0;
-hunk ./lib/gallery.php 226
-- $cnt = 0;
-+ while ($other->fetch()) {
-hunk ./lib/gallery.php 228
-- while ($other->fetch()) {
-+ $cnt++;
-hunk ./lib/gallery.php 230
-- $cnt++;
-+ if ($cnt > AVATARS_PER_PAGE) {
-+ break;
-+ }
-hunk ./lib/gallery.php 234
-- if ($cnt > AVATARS_PER_PAGE) {
-- break;
-- }
-+ common_element_start('li');
-hunk ./lib/gallery.php 236
-- common_element_start('li');
-+ common_element_start('a', array('title' => ($other->fullname) ?
-+ $other->fullname :
-+ $other->nickname,
-+ 'href' => $other->profileurl,
-+ 'class' => 'subscription'));
-+ $avatar = $other->getAvatar(AVATAR_STREAM_SIZE);
-+ common_element('img',
-+ array('src' =>
-+ (($avatar) ? common_avatar_display_url($avatar) :
-+ common_default_avatar(AVATAR_STREAM_SIZE)),
-+ 'width' => AVATAR_STREAM_SIZE,
-+ 'height' => AVATAR_STREAM_SIZE,
-+ 'class' => 'avatar stream',
-+ 'alt' => ($other->fullname) ?
-+ $other->fullname :
-+ $other->nickname));
-+ common_element_end('a');
-hunk ./lib/gallery.php 254
-- common_element_start('a', array('title' => ($other->fullname) ?
-- $other->fullname :
-- $other->nickname,
-- 'href' => $other->profileurl,
-- 'class' => 'subscription'));
-- $avatar = $other->getAvatar(AVATAR_STREAM_SIZE);
-- common_element('img',
-- array('src' =>
-- (($avatar) ? common_avatar_display_url($avatar) :
-- common_default_avatar(AVATAR_STREAM_SIZE)),
-- 'width' => AVATAR_STREAM_SIZE,
-- 'height' => AVATAR_STREAM_SIZE,
-- 'class' => 'avatar stream',
-- 'alt' => ($other->fullname) ?
-- $other->fullname :
-- $other->nickname));
-- common_element_end('a');
-+ // XXX: subscribe form here
-hunk ./lib/gallery.php 256
-- # XXX: subscribe form here
-+ common_element_end('li');
-+ }
-hunk ./lib/gallery.php 259
-- common_element_end('li');
-- }
-+ common_element_end('ul');
-hunk ./lib/gallery.php 261
-- common_element_end('ul');
--
-- return $cnt;
++ $conn->send($out);
+hunk ./lib/jabber.php 317
+-function jabber_broadcast_notice($notice) {
++/**
++ * broadcast 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
++ * have this subscription enabled for Jabber. It also sends the notice to
++ * all recipients of @-replies who have Jabber addresses and Jabber notification
++ * enabled. This is really the heart of Jabber distribution in Laconica.
++ *
++ * @param Notice $notice The notice to broadcast
++ *
++ * @return boolean success flag
++ */
+hunk ./lib/jabber.php 331
+- if (!common_config('xmpp', 'enabled')) {
+- return true;
- }
-+ return $cnt;
+- $profile = Profile::staticGet($notice->profile_id);
++function jabber_broadcast_notice($notice)
++{
++ if (!common_config('xmpp', 'enabled')) {
++ return true;
+ }
-hunk ./lib/gallery.php 264
-- function gallery_type() {
-- return NULL;
-- }
-+ function gallery_type()
-+ {
-+ return null;
++ $profile = Profile::staticGet($notice->profile_id);
++
++ if (!$profile) {
++ common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
++ 'unknown profile ' . common_log_objstring($notice),
++ __FILE__);
++ return false;
+ }
-hunk ./lib/gallery.php 269
-- function get_instructions(&$profile) {
-- return NULL;
+hunk ./lib/jabber.php 345
+- if (!$profile) {
+- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
+- 'unknown profile ' . common_log_objstring($notice),
+- __FILE__);
+- return false;
- }
-+ function get_instructions(&$profile)
-+ {
-+ return null;
-+ }
-hunk ./lib/gallery.php 274
-- function fields() {
-- return NULL;
++ $msg = jabber_format_notice($profile, $notice);
++ $entry = jabber_format_entry($profile, $notice);
+hunk ./lib/jabber.php 348
+- $msg = jabber_format_notice($profile, $notice);
+- $entry = jabber_format_entry($profile, $notice);
++ $profile->free();
++ unset($profile);
+hunk ./lib/jabber.php 351
+- $profile->free();
+- unset($profile);
++ $sent_to = array();
+hunk ./lib/jabber.php 353
+- $sent_to = array();
+- $conn = jabber_connect();
++ $conn = jabber_connect();
+hunk ./lib/jabber.php 355
+- # First, get users to whom this is a direct reply
+- $user = new User();
+- $user->query('SELECT user.id, user.jabber ' .
+- 'FROM user JOIN reply ON user.id = reply.profile_id ' .
+- 'WHERE reply.notice_id = ' . $notice->id . ' ' .
+- 'AND user.jabber is not null ' .
+- 'AND user.jabbernotify = 1 ' .
+- 'AND user.jabberreplies = 1 ');
++ // First, get users to whom this is a direct reply
++ $user = new User();
++ $user->query('SELECT user.id, user.jabber ' .
++ 'FROM user JOIN reply ON user.id = reply.profile_id ' .
++ 'WHERE reply.notice_id = ' . $notice->id . ' ' .
++ 'AND user.jabber is not null ' .
++ 'AND user.jabbernotify = 1 ' .
++ 'AND user.jabberreplies = 1 ');
+hunk ./lib/jabber.php 364
+- 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;
- }
-+ function fields()
-+ {
-+ return null;
++ 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;
+ }
-hunk ./lib/gallery.php 279
-- function div_class() {
-- return '';
+hunk ./lib/jabber.php 373
+- $user->free();
++ $user->free();
+hunk ./lib/jabber.php 375
+- # Now, get users subscribed to this profile
++ // Now, get users subscribed to this profile
+hunk ./lib/jabber.php 377
+- $user = new User();
+- $user->query('SELECT user.id, user.jabber ' .
+- 'FROM user JOIN subscription ON user.id = subscription.subscriber ' .
+- 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
+- 'AND user.jabber is not null ' .
+- 'AND user.jabbernotify = 1 ' .
++ $user = new User();
++ $user->query('SELECT user.id, user.jabber ' .
++ 'FROM user JOIN subscription ' .
++ 'ON user.id = subscription.subscriber ' .
++ 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
++ 'AND user.jabber is not null ' .
++ 'AND user.jabbernotify = 1 ' .
+hunk ./lib/jabber.php 386
+- 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);
+- }
- }
-+ function div_class()
-+ {
-+ return '';
++ 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);
++ }
+ }
-hunk ./lib/gallery.php 284
-- function display_links($profile, $page, $display) {
-- $tag = $this->trimmed('tag');
-+ function display_links($profile, $page, $display)
-+ {
-+ $tag = $this->trimmed('tag');
-hunk ./lib/gallery.php 288
-- common_element_start('dl', array('id'=>'subscriptions_nav'));
-- common_element('dt', null, _('Subscriptions navigation'));
-- common_element_start('dd');
-- common_element_start('ul', array('class'=>'nav'));
-+ common_element_start('dl', array('id'=>'subscriptions_nav'));
-+ common_element('dt', null, _('Subscriptions navigation'));
-+ common_element_start('dd');
-+ common_element_start('ul', array('class'=>'nav'));
-hunk ./lib/gallery.php 293
-- switch ($display) {
-- case 'list':
-- common_element('li', array('class'=>'child_1'), _('List'));
-- common_element_start('li');
-- $url_args = array('display' => 'icons',
-- 'nickname' => $profile->nickname,
-- 'page' => 1 + floor((($page - 1) * PROFILES_PER_PAGE) / AVATARS_PER_PAGE));
-- if ($tag) {
-- $url_args['tag'] = $tag;
-- }
-- $url = common_local_url($this->trimmed('action'), $url_args);
-- common_element('a', array('href' => $url),
-- _('Icons'));
-- common_element_end('li');
-- break;
-- default:
-- common_element_start('li', array('class'=>'child_1'));
-- $url_args = array('nickname' => $profile->nickname,
-- 'page' => 1 + floor((($page - 1) * AVATARS_PER_PAGE) / PROFILES_PER_PAGE));
-- if ($tag) {
-- $url_args['tag'] = $tag;
-- }
-- $url = common_local_url($this->trimmed('action'), $url_args);
-- common_element('a', array('href' => $url),
-- _('List'));
-- common_element_end('li');
-- common_element('li', NULL, _('Icons'));
-- break;
+hunk ./lib/jabber.php 398
+- $user->free();
++ $user->free();
+hunk ./lib/jabber.php 400
+- return true;
++ return true;
+hunk ./lib/jabber.php 403
+-function jabber_public_notice($notice) {
++/**
++ * send 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.
++ *
++ * @param Notice $notice notice to broadcast
++ *
++ * @return boolean success flag
++ */
+hunk ./lib/jabber.php 414
+- # Now, users who want everything
++function jabber_public_notice($notice)
++{
++ // Now, users who want everything
+hunk ./lib/jabber.php 418
+- $public = common_config('xmpp', 'public');
++ $public = common_config('xmpp', 'public');
+hunk ./lib/jabber.php 420
+- # FIXME PRIV don't send out private messages here
+- # XXX: should we send out non-local messages if public,localonly
+- # = false? I think not
++ // FIXME PRIV don't send out private messages here
++ // XXX: should we send out non-local messages if public,localonly
++ // = false? I think not
+hunk ./lib/jabber.php 424
+- if ($public && $notice->is_local) {
+- $profile = Profile::staticGet($notice->profile_id);
++ if ($public && $notice->is_local) {
++ $profile = Profile::staticGet($notice->profile_id);
+hunk ./lib/jabber.php 427
+- if (!$profile) {
+- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
+- 'unknown profile ' . common_log_objstring($notice),
+- __FILE__);
+- return false;
- }
-+ switch ($display) {
-+ case 'list':
-+ common_element('li', array('class'=>'child_1'), _('List'));
-+ common_element_start('li');
-+ $url_args = array('display' => 'icons',
-+ 'nickname' => $profile->nickname,
-+ 'page' => 1 + floor((($page - 1) * PROFILES_PER_PAGE) / AVATARS_PER_PAGE));
-+ if ($tag) {
-+ $url_args['tag'] = $tag;
-+ }
-+ $url = common_local_url($this->trimmed('action'), $url_args);
-+ common_element('a', array('href' => $url),
-+ _('Icons'));
-+ common_element_end('li');
-+ break;
-+ default:
-+ common_element_start('li', array('class'=>'child_1'));
-+ $url_args = array('nickname' => $profile->nickname,
-+ 'page' => 1 + floor((($page - 1) * AVATARS_PER_PAGE) / PROFILES_PER_PAGE));
-+ if ($tag) {
-+ $url_args['tag'] = $tag;
-+ }
-+ $url = common_local_url($this->trimmed('action'), $url_args);
-+ common_element('a', array('href' => $url),
-+ _('List'));
-+ common_element_end('li');
-+ common_element('li', null, _('Icons'));
-+ break;
++ if (!$profile) {
++ common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
++ 'unknown profile ' . common_log_objstring($notice),
++ __FILE__);
++ return false;
+ }
-hunk ./lib/gallery.php 323
-- common_element_end('ul');
-- common_element_end('dd');
-- common_element_end('dl');
-- }
-+ common_element_end('ul');
-+ common_element_end('dd');
-+ common_element_end('dl');
-+ }
-hunk ./lib/gallery.php 328
-- # Get list of tags we tagged other users with
-+ // Get list of tags we tagged other users with
-hunk ./lib/gallery.php 330
-- function get_all_tags($profile, $lst, $usr) {
-- $profile_tag = new Notice_tag();
-- $profile_tag->query('SELECT DISTINCT(tag) ' .
-- 'FROM profile_tag, subscription ' .
-- 'WHERE tagger = ' . $profile->id . ' ' .
-- 'AND ' . $usr . ' = ' . $profile->id . ' ' .
-- 'AND ' . $lst . ' = tagged ' .
-- 'AND tagger != tagged');
-- $tags = array();
-- while ($profile_tag->fetch()) {
-- $tags[] = $profile_tag->tag;
+hunk ./lib/jabber.php 434
+- $msg = jabber_format_notice($profile, $notice);
+- $entry = jabber_format_entry($profile, $notice);
++ $msg = jabber_format_notice($profile, $notice);
++ $entry = jabber_format_entry($profile, $notice);
+hunk ./lib/jabber.php 437
+- $conn = jabber_connect();
++ $conn = jabber_connect();
+hunk ./lib/jabber.php 439
+- foreach ($public as $address) {
+- common_log(LOG_INFO,
+- 'Sending notice ' . $notice->id . ' to public listener ' . $address,
+- __FILE__);
+- $conn->message($address, $msg, 'chat', NULL, $entry);
+- $conn->processTime(0);
- }
-- $profile_tag->free();
-- return $tags;
+- $profile->free();
- }
-+ function get_all_tags($profile, $lst, $usr)
-+ {
-+ $profile_tag = new Notice_tag();
-+ $profile_tag->query('SELECT DISTINCT(tag) ' .
-+ 'FROM profile_tag, subscription ' .
-+ 'WHERE tagger = ' . $profile->id . ' ' .
-+ 'AND ' . $usr . ' = ' . $profile->id . ' ' .
-+ 'AND ' . $lst . ' = tagged ' .
-+ 'AND tagger != tagged');
-+ $tags = array();
-+ while ($profile_tag->fetch()) {
-+ $tags[] = $profile_tag->tag;
++ foreach ($public as $address) {
++ common_log(LOG_INFO,
++ 'Sending notice ' . $notice->id .
++ ' to public listener ' . $address,
++ __FILE__);
++ $conn->message($address, $msg, 'chat', null, $entry);
++ $conn->processTime(0);
+ }
-+ $profile_tag->free();
-+ return $tags;
++ $profile->free();
+ }
+hunk ./lib/jabber.php 450
+- return true;
++ return true;
+hunk ./lib/jabber.php 453
+-function jabber_format_notice(&$profile, &$notice) {
+- return $profile->nickname . ': ' . $notice->content;
++/**
++ * makes a plain-text formatted version of a notice, suitable for Jabber distribution
++ *
++ * @param Profile &$profile profile of the sending user
++ * @param Notice &$notice notice being sent
++ *
++ * @return string plain-text version of the notice, with user nickname prefixed
++ */
++
++function jabber_format_notice(&$profile, &$notice)
++{
++ return $profile->nickname . ': ' . $notice->content;
diff --git a/lib/jabber.php b/lib/jabber.php
index ab0fd6af8..099ded9eb 100644
--- a/lib/jabber.php
+++ b/lib/jabber.php
@@ -1,9 +1,12 @@
<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+/**
+ * Laconica, the distributed open-source microblogging tool
*
- * This program is free software: you can redistribute it and/or modify
+ * utility functions for Jabber/GTalk/XMPP messages
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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.
@@ -15,286 +18,448 @@
*
* 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/>.
+ *
+ * @category Network
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2008 Control Yourself, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://laconi.ca/
*/
-if (!defined('LACONICA')) { exit(1); }
+if (!defined('LACONICA')) {
+ exit(1);
+}
-require_once('XMPPHP/XMPP.php');
+require_once 'XMPPHP/XMPP.php';
-function jabber_valid_base_jid($jid) {
- # Cheap but effective
- return Validate::email($jid);
-}
+/**
+ * checks whether a string is a syntactically valid Jabber ID (JID)
+ *
+ * @param string $jid string to check
+ *
+ * @return boolean whether the string is a valid JID
+ */
-function jabber_normalize_jid($jid) {
- if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) {
- $node = $matches[1];
- $server = $matches[2];
- return strtolower($node.'@'.$server);
- } else {
- return NULL;
- }
+function jabber_valid_base_jid($jid)
+{
+ // Cheap but effective
+ return Validate::email($jid);
}
-function jabber_daemon_address() {
- return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
+/**
+ * normalizes a Jabber ID for comparison
+ *
+ * @param string $jid JID to check
+ *
+ * @return string an equivalent JID in normalized (lowercase) form
+ */
+
+function jabber_normalize_jid($jid)
+{
+ if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) {
+ $node = $matches[1];
+ $server = $matches[2];
+ return strtolower($node.'@'.$server);
+ } else {
+ return null;
+ }
}
-function jabber_connect($resource=NULL) {
- static $conn = NULL;
- if (!$conn) {
- $conn = new XMPPHP_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'),
- common_config('xmpp', 'server'),
- common_config('xmpp', 'debug') ?
- true : false,
- common_config('xmpp', 'debug') ?
- XMPPHP_Log::LEVEL_VERBOSE : NULL
- );
-
- if (!$conn) {
- return false;
- }
-
- $conn->autoSubscribe();
- $conn->useEncryption(common_config('xmpp', 'encryption'));
-
- try {
- $conn->connect(true); # true = persistent connection
- } catch (XMPPHP_Exception $e) {
- common_log(LOG_ERROR, $e->getMessage());
- return false;
- }
-
- $conn->processUntil('session_start');
- }
- return $conn;
+/**
+ * the JID of the Jabber daemon for this Laconica instance
+ *
+ * @return string JID of the Jabber daemon
+ */
+
+function jabber_daemon_address()
+{
+ return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
}
-function jabber_send_notice($to, $notice) {
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
- $profile = Profile::staticGet($notice->profile_id);
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to send notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return false;
- }
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
- $conn->message($to, $msg, 'chat', NULL, $entry);
- $profile->free();
- return true;
+/**
+ * connect the configured Jabber account to the configured server
+ *
+ * @param string $resource Resource to connect (defaults to configured resource)
+ *
+ * @return XMPPHP connection to the configured server
+ */
+
+function jabber_connect($resource=null)
+{
+ static $conn = null;
+ if (!$conn) {
+ $conn = new XMPPHP_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'),
+ common_config('xmpp', 'server'),
+ common_config('xmpp', 'debug') ?
+ true : false,
+ common_config('xmpp', 'debug') ?
+ XMPPHP_Log::LEVEL_VERBOSE : null
+ );
+
+ if (!$conn) {
+ return false;
+ }
+
+ $conn->autoSubscribe();
+ $conn->useEncryption(common_config('xmpp', 'encryption'));
+
+ try {
+ $conn->connect(true); // true = persistent connection
+ } catch (XMPPHP_Exception $e) {
+ common_log(LOG_ERROR, $e->getMessage());
+ return false;
+ }
+
+ $conn->processUntil('session_start');
+ }
+ return $conn;
}
-# Extra stuff defined by Twitter, needed by twitter clients
-
-function jabber_format_entry($profile, $notice) {
-
- # FIXME: notice url might be remote
-
- $noticeurl = common_local_url('shownotice',
- array('notice' => $notice->id));
- $msg = jabber_format_notice($profile, $notice);
- $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n";
- $entry .= "<source>\n";
- $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n";
- $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n";
- $entry .= "<link rel='self' type='application/rss+xml' href='" . common_local_url('userrss', array('nickname' => $profile->nickname)) . "'/>\n";
- $entry .= "<author><name>" . $profile->nickname . "</name></author>\n";
- $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n";
- $entry .= "</source>\n";
- $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n";
- $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n";
- $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n";
- $entry .= "<id>". $notice->uri . "</id>\n";
- $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n";
- $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n";
- $entry .= "</entry>\n";
-
- $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n";
- $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n";
- $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: ";
- $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice);
- $html .= "\n</body>\n";
- $html .= "\n</html>\n";
-
- $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n";
- $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n";
- $address .= "</addresses>\n";
-
- # FIXME: include a pubsub event, too.
-
- return $html . $entry . $address;
+/**
+ * send a single notice to a given Jabber address
+ *
+ * @param string $to JID to send the notice to
+ * @param Notice $notice notice to send
+ *
+ * @return boolean success value
+ */
+
+function jabber_send_notice($to, $notice)
+{
+ $conn = jabber_connect();
+ if (!$conn) {
+ return false;
+ }
+ $profile = Profile::staticGet($notice->profile_id);
+ if (!$profile) {
+ common_log(LOG_WARNING, 'Refusing to send notice with ' .
+ 'unknown profile ' . common_log_objstring($notice),
+ __FILE__);
+ return false;
+ }
+ $msg = jabber_format_notice($profile, $notice);
+ $entry = jabber_format_entry($profile, $notice);
+ $conn->message($to, $msg, 'chat', null, $entry);
+ $profile->free();
+ return true;
}
-function jabber_send_message($to, $body, $type='chat', $subject=NULL) {
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
- $conn->message($to, $body, $type, $subject);
- return true;
+/**
+ * extra information for XMPP messages, as defined by Twitter
+ *
+ * @param Profile $profile Profile of the sending user
+ * @param Notice $notice Notice being sent
+ *
+ * @return string Extra information (Atom, HTML, addresses) in string format
+ */
+
+function jabber_format_entry($profile, $notice)
+{
+ // FIXME: notice url might be remote
+
+ $noticeurl = common_local_url('shownotice',
+ array('notice' => $notice->id));
+
+ $msg = jabber_format_notice($profile, $notice);
+
+ $self_url = common_local_url('userrss', array('nickname' => $profile->nickname));
+
+ $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n";
+ $entry .= "<source>\n";
+ $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n";
+ $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n";
+ $entry .= "<link rel='self' type='application/rss+xml' href='" . $self_url . "'/>\n";
+ $entry .= "<author><name>" . $profile->nickname . "</name></author>\n";
+ $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n";
+ $entry .= "</source>\n";
+ $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n";
+ $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n";
+ $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n";
+ $entry .= "<id>". $notice->uri . "</id>\n";
+ $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n";
+ $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n";
+ $entry .= "</entry>\n";
+
+ $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n";
+ $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n";
+ $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: ";
+ $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice);
+ $html .= "\n</body>\n";
+ $html .= "\n</html>\n";
+
+ $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n";
+ $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n";
+ $address .= "</addresses>\n";
+
+ // FIXME: include a pubsub event, too.
+
+ return $html . $entry . $address;
}
-function jabber_send_presence($status, $show='available', $to=NULL,
- $type = 'available', $priority=NULL)
+/**
+ * sends a single text message to a given JID
+ *
+ * @param string $to JID to send the message to
+ * @param string $body body of the message
+ * @param string $type type of the message
+ * @param string $subject subject of the message
+ *
+ * @return boolean success flag
+ */
+
+function jabber_send_message($to, $body, $type='chat', $subject=null)
{
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
- $conn->presence($status, $show, $to, $type, $priority);
- return true;
+ $conn = jabber_connect();
+ if (!$conn) {
+ return false;
+ }
+ $conn->message($to, $body, $type, $subject);
+ return true;
}
-function jabber_confirm_address($code, $nickname, $address) {
- $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' .
- 'has said that your Jabber ID belongs to them. ' .
- 'If that\'s true, you can confirm by clicking on this URL: ' .
- common_local_url('confirmaddress', array('code' => $code)) .
- ' . (If you cannot click it, copy-and-paste it into the ' .
- 'address bar of your browser). If that user isn\'t you, ' .
- 'or if you didn\'t request this confirmation, just ignore this message.';
+/**
+ * sends a presence stanza on the Jabber network
+ *
+ * @param string $status current status, free-form string
+ * @param string $show structured status value
+ * @param string $to recipient of presence, null for general
+ * @param string $type type of status message, related to $show
+ * @param int $priority priority of the presence
+ *
+ * @return boolean success value
+ */
- return jabber_send_message($address, $body);
+function jabber_send_presence($status, $show='available', $to=null,
+ $type = 'available', $priority=null)
+{
+ $conn = jabber_connect();
+ if (!$conn) {
+ return false;
+ }
+ $conn->presence($status, $show, $to, $type, $priority);
+ return true;
}
-function jabber_special_presence($type, $to=NULL, $show=NULL, $status=NULL) {
- $conn = jabber_connect();
-
- $to = htmlspecialchars($to);
- $status = htmlspecialchars($status);
- $out = "<presence";
- if($to) $out .= " to='$to'";
- if($type) $out .= " type='$type'";
- if($show == 'available' and !$status) {
- $out .= "/>";
- } else {
- $out .= ">";
- if($show && ($show != 'available')) $out .= "<show>$show</show>";
- if($status) $out .= "<status>$status</status>";
- $out .= "</presence>";
- }
- $conn->send($out);
+/**
+ * sends a confirmation request to a JID
+ *
+ * @param string $code confirmation code for confirmation URL
+ * @param string $nickname nickname of confirming user
+ * @param string $address JID to send confirmation to
+ *
+ * @return boolean success flag
+ */
+
+function jabber_confirm_address($code, $nickname, $address)
+{
+ $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' .
+ 'has said that your Jabber ID belongs to them. ' .
+ 'If that\'s true, you can confirm by clicking on this URL: ' .
+ common_local_url('confirmaddress', array('code' => $code)) .
+ ' . (If you cannot click it, copy-and-paste it into the ' .
+ 'address bar of your browser). If that user isn\'t you, ' .
+ 'or if you didn\'t request this confirmation, just ignore this message.';
+
+ return jabber_send_message($address, $body);
}
-function jabber_broadcast_notice($notice) {
-
- if (!common_config('xmpp', 'enabled')) {
- return true;
- }
- $profile = Profile::staticGet($notice->profile_id);
-
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return false;
- }
-
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
-
- $profile->free();
- unset($profile);
-
- $sent_to = array();
- $conn = jabber_connect();
-
- # First, get users to whom this is a direct reply
- $user = new User();
- $user->query('SELECT user.id, user.jabber ' .
- 'FROM user JOIN reply ON user.id = reply.profile_id ' .
- 'WHERE reply.notice_id = ' . $notice->id . ' ' .
- 'AND user.jabber is not null ' .
- 'AND user.jabbernotify = 1 ' .
- 'AND user.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 user.id, user.jabber ' .
- 'FROM user JOIN subscription ON user.id = subscription.subscriber ' .
- 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
- 'AND user.jabber is not null ' .
- 'AND user.jabbernotify = 1 ' .
- 'AND subscription.jabber = 1 ');
+/**
+ * sends a "special" presence stanza on the Jabber network
+ *
+ * @param string $type Type of presence
+ * @param string $to JID to send presence to
+ * @param string $show show value for presence
+ * @param string $status status value for presence
+ *
+ * @return boolean success flag
+ *
+ * @see jabber_send_presence()
+ */
- 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);
- }
- }
+function jabber_special_presence($type, $to=null, $show=null, $status=null)
+{
+ // FIXME: why use this instead of jabber_send_presence()?
+ $conn = jabber_connect();
+
+ $to = htmlspecialchars($to);
+ $status = htmlspecialchars($status);
+
+ $out = "<presence";
+ if ($to) {
+ $out .= " to='$to'";
+ }
+ if ($type) {
+ $out .= " type='$type'";
+ }
+ if ($show == 'available' and !$status) {
+ $out .= "/>";
+ } else {
+ $out .= ">";
+ if ($show && ($show != 'available')) {
+ $out .= "<show>$show</show>";
+ }
+ if ($status) {
+ $out .= "<status>$status</status>";
+ }
+ $out .= "</presence>";
+ }
+ $conn->send($out);
+}
- $user->free();
+/**
+ * broadcast 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
+ * have this subscription enabled for Jabber. It also sends the notice to
+ * all recipients of @-replies who have Jabber addresses and Jabber notification
+ * enabled. This is really the heart of Jabber distribution in Laconica.
+ *
+ * @param Notice $notice The notice to broadcast
+ *
+ * @return boolean success flag
+ */
- return true;
+function jabber_broadcast_notice($notice)
+{
+ if (!common_config('xmpp', 'enabled')) {
+ return true;
+ }
+ $profile = Profile::staticGet($notice->profile_id);
+
+ if (!$profile) {
+ common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
+ 'unknown profile ' . common_log_objstring($notice),
+ __FILE__);
+ return false;
+ }
+
+ $msg = jabber_format_notice($profile, $notice);
+ $entry = jabber_format_entry($profile, $notice);
+
+ $profile->free();
+ unset($profile);
+
+ $sent_to = array();
+
+ $conn = jabber_connect();
+
+ // First, get users to whom this is a direct reply
+ $user = new User();
+ $user->query('SELECT user.id, user.jabber ' .
+ 'FROM user JOIN reply ON user.id = reply.profile_id ' .
+ 'WHERE reply.notice_id = ' . $notice->id . ' ' .
+ 'AND user.jabber is not null ' .
+ 'AND user.jabbernotify = 1 ' .
+ 'AND user.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 user.id, user.jabber ' .
+ 'FROM user JOIN subscription ' .
+ 'ON user.id = subscription.subscriber ' .
+ 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
+ 'AND user.jabber is not null ' .
+ 'AND user.jabbernotify = 1 ' .
+ 'AND subscription.jabber = 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);
+ }
+ }
+
+ $user->free();
+
+ return true;
}
-function jabber_public_notice($notice) {
+/**
+ * send 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.
+ *
+ * @param Notice $notice notice to broadcast
+ *
+ * @return boolean success flag
+ */
- # Now, users who want everything
+function jabber_public_notice($notice)
+{
+ // Now, users who want everything
- $public = common_config('xmpp', 'public');
+ $public = common_config('xmpp', 'public');
- # FIXME PRIV don't send out private messages here
- # XXX: should we send out non-local messages if public,localonly
- # = false? I think not
+ // 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) {
- $profile = Profile::staticGet($notice->profile_id);
+ if ($public && $notice->is_local) {
+ $profile = Profile::staticGet($notice->profile_id);
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return false;
- }
+ if (!$profile) {
+ common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
+ 'unknown profile ' . common_log_objstring($notice),
+ __FILE__);
+ return false;
+ }
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
+ $msg = jabber_format_notice($profile, $notice);
+ $entry = jabber_format_entry($profile, $notice);
- $conn = jabber_connect();
+ $conn = jabber_connect();
- foreach ($public as $address) {
- common_log(LOG_INFO,
- 'Sending notice ' . $notice->id . ' to public listener ' . $address,
- __FILE__);
- $conn->message($address, $msg, 'chat', NULL, $entry);
- $conn->processTime(0);
- }
- $profile->free();
- }
+ foreach ($public as $address) {
+ common_log(LOG_INFO,
+ 'Sending notice ' . $notice->id .
+ ' to public listener ' . $address,
+ __FILE__);
+ $conn->message($address, $msg, 'chat', null, $entry);
+ $conn->processTime(0);
+ }
+ $profile->free();
+ }
- return true;
+ return true;
}
-function jabber_format_notice(&$profile, &$notice) {
- return $profile->nickname . ': ' . $notice->content;
+/**
+ * makes a plain-text formatted version of a notice, suitable for Jabber distribution
+ *
+ * @param Profile &$profile profile of the sending user
+ * @param Notice &$notice notice being sent
+ *
+ * @return string plain-text version of the notice, with user nickname prefixed
+ */
+
+function jabber_format_notice(&$profile, &$notice)
+{
+ return $profile->nickname . ': ' . $notice->content;
}