MeteorPlugin.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. const PLUGIN_VERSION = '2.0.0';
  45. public $webserver = null;
  46. public $webport = null;
  47. public $controlport = null;
  48. public $controlserver = null;
  49. public $channelbase = null;
  50. public $protocol = null;
  51. public $persistent = true;
  52. protected $_socket = null;
  53. function __construct($webserver=null, $webport=4670, $controlport=4671, $controlserver=null, $channelbase='', $protocol='http')
  54. {
  55. global $config;
  56. $this->webserver = (empty($webserver)) ? $config['site']['server'] : $webserver;
  57. $this->webport = $webport;
  58. $this->controlport = $controlport;
  59. $this->controlserver = (empty($controlserver)) ? $webserver : $controlserver;
  60. $this->channelbase = $channelbase;
  61. $this->protocol = $protocol;
  62. parent::__construct();
  63. }
  64. /**
  65. * Pull settings from config file/database if set.
  66. */
  67. function initialize()
  68. {
  69. $settings = array('webserver',
  70. 'webport',
  71. 'controlport',
  72. 'controlserver',
  73. 'channelbase',
  74. 'protocol');
  75. foreach ($settings as $name) {
  76. $val = common_config('meteor', $name);
  77. if ($val !== false) {
  78. $this->$name = $val;
  79. }
  80. }
  81. return parent::initialize();
  82. }
  83. function _getScripts()
  84. {
  85. $scripts = parent::_getScripts();
  86. if ($this->protocol == 'https') {
  87. $scripts[] = 'https://'.$this->webserver.(($this->webport == 443) ? '':':'.$this->webport).'/meteor.js';
  88. } else {
  89. $scripts[] = 'http://'.$this->webserver.(($this->webport == 80) ? '':':'.$this->webport).'/meteor.js';
  90. }
  91. $scripts[] = $this->path('js/meteorupdater.js');
  92. return $scripts;
  93. }
  94. function _updateInitialize($timeline, $user_id)
  95. {
  96. $script = parent::_updateInitialize($timeline, $user_id);
  97. $ours = sprintf("MeteorUpdater.init(%s, %s, %s, %s);",
  98. json_encode($this->webserver),
  99. json_encode($this->webport),
  100. json_encode($this->protocol),
  101. json_encode($timeline));
  102. return $script." ".$ours;
  103. }
  104. function _connect()
  105. {
  106. $controlserver = (empty($this->controlserver)) ? $this->webserver : $this->controlserver;
  107. $errno = $errstr = null;
  108. $timeout = 5;
  109. $flags = STREAM_CLIENT_CONNECT;
  110. if ($this->persistent) $flags |= STREAM_CLIENT_PERSISTENT;
  111. // May throw an exception.
  112. $this->_socket = stream_socket_client("tcp://{$controlserver}:{$this->controlport}", $errno, $errstr, $timeout, $flags);
  113. if (!$this->_socket) {
  114. // TRANS: Exception. %1$s is the control server, %2$s is the control port.
  115. throw new Exception(sprintf(_m('Could not connect to %1$s on %2$s.'),$controlserver,$this->controlport));
  116. }
  117. }
  118. function _publish($channel, $message)
  119. {
  120. $message = json_encode($message);
  121. $message = addslashes($message);
  122. $cmd = "ADDMESSAGE $channel $message\n";
  123. $cnt = fwrite($this->_socket, $cmd);
  124. $result = fgets($this->_socket);
  125. if (preg_match('/^ERR (.*)$/', $result, $matches)) {
  126. // TRANS: Exception. %s is the Meteor message that could not be added.
  127. throw new Exception(sprintf(_m('Error adding meteor message "%s".'),$matches[1]));
  128. }
  129. // TODO: parse and deal with result
  130. }
  131. function _disconnect()
  132. {
  133. if (!$this->persistent) {
  134. $cnt = fwrite($this->_socket, "QUIT\n");
  135. @fclose($this->_socket);
  136. }
  137. }
  138. // Meteord flips out with default '/' separator
  139. function _pathToChannel($path)
  140. {
  141. if (!empty($this->channelbase)) {
  142. array_unshift($path, $this->channelbase);
  143. }
  144. return implode('-', $path);
  145. }
  146. public function onPluginVersion(array &$versions): bool
  147. {
  148. $versions[] = array('name' => 'Meteor',
  149. 'version' => self::PLUGIN_VERSION,
  150. 'author' => 'Evan Prodromou',
  151. 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Meteor',
  152. 'rawdescription' =>
  153. // TRANS: Plugin description.
  154. _m('Plugin to do "real time" updates using Meteor.'));
  155. return true;
  156. }
  157. }