MeteorPlugin.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <?php
  2. /**
  3. * StatusNet, the distributed open-source microblogging tool
  4. *
  5. * Plugin to do "real time" updates using Meteor
  6. *
  7. * PHP version 5
  8. *
  9. * LICENCE: This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * @category Plugin
  23. * @package StatusNet
  24. * @author Evan Prodromou <evan@status.net>
  25. * @copyright 2009 StatusNet, Inc.
  26. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  27. * @link http://status.net/
  28. */
  29. if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
  30. exit(1);
  31. }
  32. require_once INSTALLDIR.'/plugins/Realtime/RealtimePlugin.php';
  33. /**
  34. * Plugin to do realtime updates using Meteor
  35. *
  36. * @category Plugin
  37. * @package StatusNet
  38. * @author Evan Prodromou <evan@status.net>
  39. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  40. * @link http://status.net/
  41. */
  42. class MeteorPlugin extends RealtimePlugin
  43. {
  44. public $webserver = null;
  45. public $webport = null;
  46. public $controlport = null;
  47. public $controlserver = null;
  48. public $channelbase = null;
  49. public $protocol = null;
  50. public $persistent = true;
  51. protected $_socket = null;
  52. function __construct($webserver=null, $webport=4670, $controlport=4671, $controlserver=null, $channelbase='', $protocol='http')
  53. {
  54. global $config;
  55. $this->webserver = (empty($webserver)) ? $config['site']['server'] : $webserver;
  56. $this->webport = $webport;
  57. $this->controlport = $controlport;
  58. $this->controlserver = (empty($controlserver)) ? $webserver : $controlserver;
  59. $this->channelbase = $channelbase;
  60. $this->protocol = $protocol;
  61. parent::__construct();
  62. }
  63. /**
  64. * Pull settings from config file/database if set.
  65. */
  66. function initialize()
  67. {
  68. $settings = array('webserver',
  69. 'webport',
  70. 'controlport',
  71. 'controlserver',
  72. 'channelbase',
  73. 'protocol');
  74. foreach ($settings as $name) {
  75. $val = common_config('meteor', $name);
  76. if ($val !== false) {
  77. $this->$name = $val;
  78. }
  79. }
  80. return parent::initialize();
  81. }
  82. function _getScripts()
  83. {
  84. $scripts = parent::_getScripts();
  85. if ($this->protocol == 'https') {
  86. $scripts[] = 'https://'.$this->webserver.(($this->webport == 443) ? '':':'.$this->webport).'/meteor.js';
  87. } else {
  88. $scripts[] = 'http://'.$this->webserver.(($this->webport == 80) ? '':':'.$this->webport).'/meteor.js';
  89. }
  90. $scripts[] = $this->path('js/meteorupdater.js');
  91. return $scripts;
  92. }
  93. function _updateInitialize($timeline, $user_id)
  94. {
  95. $script = parent::_updateInitialize($timeline, $user_id);
  96. $ours = sprintf("MeteorUpdater.init(%s, %s, %s, %s);",
  97. json_encode($this->webserver),
  98. json_encode($this->webport),
  99. json_encode($this->protocol),
  100. json_encode($timeline));
  101. return $script." ".$ours;
  102. }
  103. function _connect()
  104. {
  105. $controlserver = (empty($this->controlserver)) ? $this->webserver : $this->controlserver;
  106. $errno = $errstr = null;
  107. $timeout = 5;
  108. $flags = STREAM_CLIENT_CONNECT;
  109. if ($this->persistent) $flags |= STREAM_CLIENT_PERSISTENT;
  110. // May throw an exception.
  111. $this->_socket = stream_socket_client("tcp://{$controlserver}:{$this->controlport}", $errno, $errstr, $timeout, $flags);
  112. if (!$this->_socket) {
  113. // TRANS: Exception. %1$s is the control server, %2$s is the control port.
  114. throw new Exception(sprintf(_m('Could not connect to %1$s on %2$s.'),$controlserver,$this->controlport));
  115. }
  116. }
  117. function _publish($channel, $message)
  118. {
  119. $message = json_encode($message);
  120. $message = addslashes($message);
  121. $cmd = "ADDMESSAGE $channel $message\n";
  122. $cnt = fwrite($this->_socket, $cmd);
  123. $result = fgets($this->_socket);
  124. if (preg_match('/^ERR (.*)$/', $result, $matches)) {
  125. // TRANS: Exception. %s is the Meteor message that could not be added.
  126. throw new Exception(sprintf(_m('Error adding meteor message "%s".'),$matches[1]));
  127. }
  128. // TODO: parse and deal with result
  129. }
  130. function _disconnect()
  131. {
  132. if (!$this->persistent) {
  133. $cnt = fwrite($this->_socket, "QUIT\n");
  134. @fclose($this->_socket);
  135. }
  136. }
  137. // Meteord flips out with default '/' separator
  138. function _pathToChannel($path)
  139. {
  140. if (!empty($this->channelbase)) {
  141. array_unshift($path, $this->channelbase);
  142. }
  143. return implode('-', $path);
  144. }
  145. function onPluginVersion(&$versions)
  146. {
  147. $versions[] = array('name' => 'Meteor',
  148. 'version' => GNUSOCIAL_VERSION,
  149. 'author' => 'Evan Prodromou',
  150. 'homepage' => 'http://status.net/wiki/Plugin:Meteor',
  151. 'rawdescription' =>
  152. // TRANS: Plugin description.
  153. _m('Plugin to do "real time" updates using Meteor.'));
  154. return true;
  155. }
  156. }