diff --git a/plugins/Irc/Fake_Irc.php b/plugins/Irc/Fake_Irc.php index a1e296e8b1..71892abf6e 100644 --- a/plugins/Irc/Fake_Irc.php +++ b/plugins/Irc/Fake_Irc.php @@ -35,6 +35,6 @@ class Fake_Irc extends Phergie_Driver_Streams { public $would_be_sent = null; protected function send($command, $args = '') { - $this->would_be_sent = array($command, $args); + $this->would_be_sent = array('command' => $command, 'args' => $args); } } diff --git a/plugins/Irc/IrcPlugin.php b/plugins/Irc/IrcPlugin.php index a9044df8d2..ce8f317843 100644 --- a/plugins/Irc/IrcPlugin.php +++ b/plugins/Irc/IrcPlugin.php @@ -60,7 +60,10 @@ class IrcPlugin extends ImPlugin { public $transporttype = null; public $encoding = null; + public $regcheck = null; + public $transport = 'irc'; + public $fake_irc; /** * Get the internationalized/translated display name of this IM service @@ -160,7 +163,33 @@ class IrcPlugin extends ImPlugin { */ public function send_message($screenname, $body) { $this->fake_irc->doPrivmsg($screenname, $body); - $this->enqueue_outgoing_raw($this->fake_irc->would_be_sent); + $this->enqueue_outgoing_raw(array('type' => 'message', 'data' => $this->fake_irc->would_be_sent)); + return true; + } + + /** + * Only sends the confirmation message if the nick is + * registered + * + * @param string $screenname screenname sending to + * @param string $code the confirmation code + * @param User $user user sending to + * @return boolean success value + */ + public function checked_send_confirmation_code($screenname, $code, $user) { + $this->fake_irc->doPrivmsg('NickServ', 'INFO '.$screenname); + $this->enqueue_outgoing_raw( + array( + 'type' => 'nickcheck', + 'data' => $this->fake_irc->would_be_sent, + 'nickdata' => + array( + 'screenname' => $screenname, + 'code' => $code, + 'user' => $user + ) + ) + ); return true; } @@ -174,6 +203,30 @@ class IrcPlugin extends ImPlugin { return true; } + /** + * Send a confirmation code to a user + * + * @param string $screenname screenname sending to + * @param string $code the confirmation code + * @param User $user user sending to + * @return boolean success value + */ + public function send_confirmation_code($screenname, $code, $user, $checked = false) { + $body = sprintf(_('User "%s" on %s has said that your %s screenname belongs to them. ' . + 'If that\'s true, you can confirm by clicking on this URL: ' . + '%s' . + ' . (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.'), + $user->nickname, common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', array('code' => $code))); + + if ($this->regcheck && !$checked) { + return $this->checked_send_confirmation_code($screenname, $code, $user); + } else { + return $this->send_message($screenname, $body); + } + } + /** * Initialize plugin * @@ -193,6 +246,29 @@ class IrcPlugin extends ImPlugin { throw new Exception('must specify a nickname'); } + if (!isset($this->port)) { + $this->port = 6667; + } + if (!isset($this->password)) { + $this->password = ''; + } + if (!isset($this->transporttype)) { + $this->transporttype = 'tcp'; + } + if (!isset($this->encoding)) { + $this->encoding = 'UTF-8'; + } + if (!isset($this->nickservpassword)) { + $this->nickservpassword = ''; + } + if (!isset($this->channels)) { + $this->channels = array(); + } + + if (!isset($this->regcheck)) { + $this->regcheck = true; + } + $this->fake_irc = new Fake_Irc; return true; } diff --git a/plugins/Irc/README b/plugins/Irc/README index 2429a5b81f..79ce8ff56a 100644 --- a/plugins/Irc/README +++ b/plugins/Irc/README @@ -22,6 +22,11 @@ nickservpassword: NickServ password for identification channels: Channels for bot to idle in transporttype: Set to 'ssl' to enable SSL encoding: Set to change encoding +regcheck: Check user's nicknames are registered, enabled by default, set to false to disable +regregexp: Override existing regexp matching response from NickServ if nick checked is registered. + Must contain a capturing group catching the nick +unregregexp: Override existing regexp matching response from NickServ if nick checked is unregistered + Must contain a capturing group catching the nick * required diff --git a/plugins/Irc/extlib/phergie/Phergie/Driver/Statusnet.php b/plugins/Irc/extlib/phergie/Phergie/Driver/Statusnet.php index 48540069ce..6a0cbb8d15 100644 --- a/plugins/Irc/extlib/phergie/Phergie/Driver/Statusnet.php +++ b/plugins/Irc/extlib/phergie/Phergie/Driver/Statusnet.php @@ -42,6 +42,19 @@ class Phergie_Driver_Statusnet extends Phergie_Driver_Streams { return parent::send($command, $args); } + public function forceQuit() { + try { + // Send a QUIT command to the server + $this->send('QUIT', 'Reconnecting'); + } catch (Phergie_Driver_Exception $e){} + + // Terminate the socket connection + fclose($this->socket); + + // Remove the socket from the internal socket list + unset($this->sockets[(string) $this->getConnection()->getHostmask()]); + } + /** * Returns the array of sockets * diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php index ff181d94e2..f873c753bb 100644 --- a/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php @@ -65,7 +65,7 @@ class Phergie_Plugin_NickServ extends Phergie_Plugin_Abstract // Get the identify message $this->identifyMessage = $this->config['nickserv.identify_message']; if (!$this->identifyMessage) { - $this->identifyMessage = 'This nickname is registered.'; + $this->identifyMessage = '/This nickname is registered./'; } } @@ -82,7 +82,7 @@ class Phergie_Plugin_NickServ extends Phergie_Plugin_Abstract if (strtolower($event->getNick()) == strtolower($this->botNick)) { $message = $event->getArgument(1); $nick = $this->connection->getNick(); - if (strpos($message, $this->identifyMessage) !== false) { + if (preg_match($this->identifyMessage, $message)) { $password = $this->config['nickserv.password']; if (!empty($password)) { $this->doPrivmsg($this->botNick, 'IDENTIFY ' . $password); diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Statusnet.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Statusnet.php new file mode 100644 index 0000000000..71b7b06bce --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Statusnet.php @@ -0,0 +1,98 @@ +. + * + * Calls the given Statusnet IM architecture enqueuing method to enqueue + * a new incoming message + * + * @category Phergie + * @package Phergie_Plugin_Statusnet + * @author Luke Fitzgerald + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class Phergie_Plugin_Statusnet extends Phergie_Plugin_Abstract { + /** + * Message callback details + * + * @var array + */ + protected $messageCallback; + + protected $regCallback; + + protected $tocheck = array(); + + /** + * Load callback from config + */ + public function onLoad() { + $messageCallback = $this->config['statusnet.messagecallback']; + if (is_callable($messageCallback)) { + $this->messageCallback = $messageCallback; + } else { + $this->messageCallback = NULL; + } + + $regCallback = $this->config['statusnet.regcallback']; + if (is_callable($regCallback)) { + $this->regCallback = $regCallback; + } else { + $this->regCallback = NULL; + } + + $this->unregRegexp = $this->config['statusnet.unregregexp']; + if (!$this->unregRegexp) { + $this->unregRegexp = '/\x02(.*?)\x02 (?:isn\'t|is not) registered/i'; + } + + $this->regRegexp = $this->config['statusnet.regregexp']; + if (!$this->regRegexp) { + $this->regRegexp = '/(?:\A|\x02)(\w+?)\x02? (?:\(account|is \w+?\z)/i'; + } + } + + /** + * Passes incoming messages to StatusNet + * + * @return void + */ + public function onPrivmsg() { + if ($this->messageCallback !== NULL) { + $event = $this->getEvent(); + $source = $event->getSource(); + $message = trim($event->getText()); + + call_user_func($this->messageCallback, array('sender' => $source, 'message' => $message)); + } + } + + public function onNotice() { + $event = $this->getEvent(); + if ($event->getNick() == 'NickServ') { + $message = $event->getArgument(1); + if (preg_match($this->unregRegexp, $message, $groups)) { + $nick = $groups[1]; + call_user_func($this->regCallback, array('nick' => $nick, 'registered' => false)); + } elseif (preg_match($this->regRegexp, $message, $groups)) { + $nick = $groups[1]; + call_user_func($this->regCallback, array('nick' => $nick, 'registered' => true)); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/StatusnetCallback.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/StatusnetCallback.php deleted file mode 100644 index 545ee343b8..0000000000 --- a/plugins/Irc/extlib/phergie/Phergie/Plugin/StatusnetCallback.php +++ /dev/null @@ -1,63 +0,0 @@ -. - * - * Calls the given Statusnet IM architecture enqueuing method to enqueue - * a new incoming message - * - * @category Phergie - * @package Phergie_Plugin_StatusnetCallback - * @author Luke Fitzgerald - * @copyright 2010 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 - * @link http://status.net/ - */ - -class Phergie_Plugin_StatusnetCallback extends Phergie_Plugin_Abstract { - /** - * Callback details - * - * @var array - */ - protected $callback; - - /** - * Load callback from config - */ - public function onLoad() { - $callback = $this->config['statusnetcallback.callback']; - if (is_callable($callback)) { - $this->callback = $callback; - } else { - $this->callback = NULL; - } - } - - /** - * Passes incoming messages to StatusNet - * - * @return void - */ - public function onPrivmsg() { - if ($this->callback !== NULL) { - $event = $this->getEvent(); - $source = $event->getSource(); - $message = trim($event->getText()); - - call_user_func($this->callback, array('sender' => $source, 'message' => $message)); - } - } -} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/patch.diff b/plugins/Irc/extlib/phergie/Phergie/Plugin/patch.diff new file mode 100644 index 0000000000..9cfa3c2b8b --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/patch.diff @@ -0,0 +1,22 @@ +diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php +index ff181d9..f873c75 100644 +--- a/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php ++++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php +@@ -65,7 +65,7 @@ class Phergie_Plugin_NickServ extends Phergie_Plugin_Abstract + // Get the identify message + $this->identifyMessage = $this->config['nickserv.identify_message']; + if (!$this->identifyMessage) { +- $this->identifyMessage = 'This nickname is registered.'; ++ $this->identifyMessage = '/This nickname is registered./'; + } + } + +@@ -82,7 +82,7 @@ class Phergie_Plugin_NickServ extends Phergie_Plugin_Abstract + if (strtolower($event->getNick()) == strtolower($this->botNick)) { + $message = $event->getArgument(1); + $nick = $this->connection->getNick(); +- if (strpos($message, $this->identifyMessage) !== false) { ++ if (preg_match($this->identifyMessage, $message)) { + $password = $this->config['nickserv.password']; + if (!empty($password)) { + $this->doPrivmsg($this->botNick, 'IDENTIFY ' . $password); diff --git a/plugins/Irc/extlib/phergie/Phergie/StatusnetBot.php b/plugins/Irc/extlib/phergie/Phergie/StatusnetBot.php index ba41f26db0..5fe65444b1 100644 --- a/plugins/Irc/extlib/phergie/Phergie/StatusnetBot.php +++ b/plugins/Irc/extlib/phergie/Phergie/StatusnetBot.php @@ -66,6 +66,27 @@ class Phergie_StatusnetBot extends Phergie_Bot { $this->getProcessor()->handleEvents(); } + /** + * Close the current connection and reconnect to the server + * + * @return void + */ + public function reconnect() { + $driver = $this->getDriver(); + $sockets = $driver->getSockets(); + + // Close any existing connections + try { + $driver->forceQuit(); + } catch (Phergie_Driver_Exception $e){} + try { + $driver->doConnect(); + } catch (Phergie_Driver_Exception $e){ + $driver->forceQuit(); + throw $e; + } + } + /** * Get the sockets used by the bot * diff --git a/plugins/Irc/ircmanager.php b/plugins/Irc/ircmanager.php index 741b324a98..93513df861 100644 --- a/plugins/Irc/ircmanager.php +++ b/plugins/Irc/ircmanager.php @@ -32,6 +32,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } class IrcManager extends ImManager { public $conn = null; + public $regchecks = array(); + public $regchecksLookup = array(); /** * Initialize connection to server. @@ -83,13 +85,6 @@ class IrcManager extends ImManager { if (!$this->conn) { $this->conn = new Phergie_StatusnetBot; - $port = empty($this->plugin->port) ? 6667 : $this->plugin->port; - $password = empty($this->plugin->password) ? '' : $this->plugin->password; - $transport = empty($this->plugin->transporttype) ? 'tcp' : $this->plugin->transporttype; - $encoding = empty($this->plugin->encoding) ? 'UTF-8' : $this->plugin->encoding; - $nickservpassword = empty($this->plugin->nickservpassword) ? '' : $this->plugin->nickservpassword; - $channels = empty($this->plugin->channels) ? array() : $this->plugin->channels; - $config = new Phergie_Config; $config->readArray( array( @@ -100,9 +95,9 @@ class IrcManager extends ImManager { 'username' => $this->plugin->username, 'realname' => $this->plugin->realname, 'nick' => $this->plugin->nick, - 'password' => $password, - 'transport' => $transport, - 'encoding' => $encoding + 'password' => $this->plugin->password, + 'transport' => $this->plugin->transporttype, + 'encoding' => $this->plugin->encoding ) ), @@ -114,16 +109,17 @@ class IrcManager extends ImManager { 'Pong', 'NickServ', 'AutoJoin', - 'StatusnetCallback', + 'Statusnet', ), 'plugins.autoload' => true, 'ui.enabled' => true, - 'nickserv.password' => $nickservpassword, - 'autojoin.channels' => $channels, - 'statusnetcallback.callback' => array($this, 'handle_irc_message') + 'nickserv.password' => $this->plugin->nickservpassword, + 'autojoin.channels' => $this->plugin->channels, + 'statusnet.messagecallback' => array($this, 'handle_irc_message'), + 'statusnet.regcallback' => array($this, 'handle_reg_response') ) ); @@ -146,6 +142,47 @@ class IrcManager extends ImManager { return true; } + /** + * Called via a callback when NickServ responds to + * the bots query asking if a nick is registered + * + * @param array $data Data + * @return void + */ + public function handle_reg_response($data) { + // Retrieve data + $nickdata = $this->regchecks[$data['nick']]; + + if ($data['registered']) { + // Send message + $this->plugin->send_confirmation_code($nickdata['screenname'], $nickdata['code'], $nickdata['user'], true); + } else { + $this->plugin->send_message($nickdata['screenname'], _m('Your nickname is not registered so IRC connectivity cannot be enabled')); + + $confirm = new Confirm_address(); + + $confirm->user_id = $user->id; + $confirm->address_type = $this->plugin->transport; + + if ($confirm->find(true)) { + $result = $confirm->delete(); + + if (!$result) { + common_log_db_error($confirm, 'DELETE', __FILE__); + // TRANS: Server error thrown on database error canceling IM address confirmation. + $this->serverError(_('Couldn\'t delete confirmation.')); + return; + } + } + } + + // Unset lookup value + unset($this->regchecksLookup[$nickdata['screenname']]); + + // Unset data + unset($this->regchecks[$data['nick']]); + } + /** * Send a message using the daemon * @@ -157,7 +194,22 @@ class IrcManager extends ImManager { if (!$this->conn) { return false; } - $this->conn->send($data[0], $data[1]); + if ($data['type'] != 'message') { + // Nick checking + $screenname = $data['nickdata']['screenname']; + if (isset($this->regchecksLookup[$user->nickname])) { + + } + $this->regchecks[$screenname] = $data['nickdata']; + $this->regchecksLookup[$user->nickname] = $screenname; + } + + try { + $this->conn->send($data['data']['command'], $data['data']['args']); + } catch (Phergie_Driver_Exception $e) { + $this->conn->reconnect(); + return false; + } return true; } }