]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - lib/jabber.php
Include meta charset header in saved HTML file for long OStatus messages; without...
[quix0rs-gnu-social.git] / lib / jabber.php
index 7d584ad0164edc18155e4e541f9025b14ace0294..db4e2e9a706a62f8bf905e4b34167f37416492f3 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Laconica, the distributed open-source microblogging tool
+ * StatusNet, the distributed open-source microblogging tool
  *
  * utility functions for Jabber/GTalk/XMPP messages
  *
  * 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.
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
+ * @link      http://status.net/
  */
 
-if (!defined('LACONICA')) {
+if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
@@ -67,7 +67,7 @@ function jabber_normalize_jid($jid)
 }
 
 /**
- * the JID of the Jabber daemon for this Laconica instance
+ * the JID of the Jabber daemon for this StatusNet instance
  *
  * @return string JID of the Jabber daemon
  */
@@ -77,8 +77,49 @@ function jabber_daemon_address()
     return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
 }
 
+class Sharing_XMPP extends XMPPHP_XMPP
+{
+    function getSocket()
+    {
+        return $this->socket;
+    }
+}
+
+/**
+ * Build an XMPP proxy connection that'll save outgoing messages
+ * to the 'xmppout' queue to be picked up by xmppdaemon later.
+ *
+ * If queueing is disabled, we'll grab a live connection.
+ *
+ * @return XMPPHP
+ */
+function jabber_proxy()
+{
+    if (common_config('queue', 'enabled')) {
+           $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;
+    } else {
+        return jabber_connect();
+    }
+}
+
 /**
- * connect the configured Jabber account to the configured server
+ * 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)
  *
@@ -87,16 +128,19 @@ function jabber_daemon_address()
 
 function jabber_connect($resource=null)
 {
-    static $conn = null;
-    if (!$conn) {
-        $conn = new XMPPHP_XMPP(common_config('xmpp', 'host') ?
+    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,
@@ -107,12 +151,16 @@ function jabber_connect($resource=null)
         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;
@@ -120,11 +168,11 @@ function jabber_connect($resource=null)
 
         $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
@@ -134,10 +182,7 @@ function jabber_connect($resource=null)
 
 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 ' .
@@ -176,6 +221,11 @@ function jabber_format_entry($profile, $notice)
     } else {
         $xs->raw(common_render_content($notice->content, $notice));
     }
+    $xs->text(" ");
+    $xs->element('a', array(
+        'href'=>common_local_url('conversation',
+            array('id' => $notice->conversation)).'#notice-'.$notice->id
+         ),sprintf(_('[%s]'),$notice->id));
     $xs->elementEnd('body');
     $xs->elementEnd('html');
 
@@ -197,10 +247,7 @@ function jabber_format_entry($profile, $notice)
 
 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;
 }
@@ -295,13 +342,13 @@ function jabber_special_presence($type, $to=null, $show=null, $status=null)
 }
 
 /**
- * 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
  * 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.
+ * enabled. This is really the heart of Jabber distribution in StatusNet.
  *
  * @param Notice $notice The notice to broadcast
  *
@@ -319,7 +366,7 @@ function jabber_broadcast_notice($notice)
         common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
                    'unknown profile ' . common_log_objstring($notice),
                    __FILE__);
-        return false;
+        return true; // not recoverable; discard.
     }
 
     $msg   = jabber_format_notice($profile, $notice);
@@ -330,84 +377,48 @@ function jabber_broadcast_notice($notice)
 
     $sent_to = array();
 
-    $conn = jabber_connect();
-
-    // 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
+    $conn = jabber_proxy();
 
-    $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.
@@ -427,20 +438,20 @@ function jabber_public_notice($notice)
     // 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) {
             common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
                        'unknown profile ' . common_log_objstring($notice),
                        __FILE__);
-            return false;
+            return true; // not recoverable; discard.
         }
 
         $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,
@@ -448,7 +459,6 @@ function jabber_public_notice($notice)
                        ' to public listener ' . $address,
                        __FILE__);
             $conn->message($address, $msg, 'chat', null, $entry);
-            $conn->processTime(0);
         }
         $profile->free();
     }
@@ -467,5 +477,5 @@ function jabber_public_notice($notice)
 
 function jabber_format_notice(&$profile, &$notice)
 {
-    return $profile->nickname . ': ' . $notice->content;
+    return $profile->nickname . ': ' . $notice->content . ' [' . $notice->id . ']';
 }