]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Irc/IrcPlugin.php
First commit of message throttling code
[quix0rs-gnu-social.git] / plugins / Irc / IrcPlugin.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * Send and receive notices using an IRC network
7  *
8  * PHP version 5
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Affero General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Affero General Public License for more details.
19  *
20  * You should have received a copy of the GNU Affero General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  * @category  IM
24  * @package   StatusNet
25  * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>
26  * @copyright 2010 StatusNet, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
28  * @link      http://status.net/
29  */
30
31 if (!defined('STATUSNET')) {
32     // This check helps protect against security problems;
33     // your code file can't be executed directly from the web.
34     exit(1);
35 }
36
37 // We bundle the Phergie library...
38 set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/extlib/phergie');
39
40 /**
41  * Plugin for IRC
42  *
43  * @category  Plugin
44  * @package   StatusNet
45  * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>
46  * @copyright 2010 StatusNet, Inc.
47  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
48  * @link      http://status.net/
49  */
50
51 class IrcPlugin extends ImPlugin {
52     public $host =  null;
53     public $port = null;
54     public $username = null;
55     public $realname = null;
56     public $nick = null;
57     public $password = null;
58     public $nickservidentifyregexp = null;
59     public $nickservpassword = null;
60     public $channels = null;
61     public $transporttype = null;
62     public $encoding = null;
63
64     public $regcheck = null;
65     public $unregregexp = null;
66     public $regregexp = null;
67
68     public $transport = 'irc';
69     protected $whiteList;
70     protected $fake_irc;
71
72     /**
73      * Get the internationalized/translated display name of this IM service
74      *
75      * @return string Name of service
76      */
77     public function getDisplayName() {
78         return _m('IRC');
79     }
80
81     /**
82      * Normalize a screenname for comparison
83      *
84      * @param string $screenname Screenname to normalize
85      * @return string An equivalent screenname in normalized form
86      */
87     public function normalize($screenname) {
88         $screenname = str_replace(" ","", $screenname);
89         return strtolower($screenname);
90     }
91
92     /**
93      * Get the screenname of the daemon that sends and receives messages
94      *
95      * @return string Screenname
96      */
97     public function daemon_screenname() {
98         return $this->nick;
99     }
100
101     /**
102      * Validate (ensure the validity of) a screenname
103      *
104      * @param string $screenname Screenname to validate
105      * @return boolean true if screenname is valid
106      */
107     public function validate($screenname) {
108         if (preg_match('/\A[a-z0-9\-_]{1,1000}\z/i', $screenname)) {
109             return true;
110         } else {
111             return false;
112         }
113     }
114
115     /**
116      * Load related modules when needed
117      *
118      * @param string $cls Name of the class to be loaded
119      * @return boolean hook value; true means continue processing, false means stop.
120      */
121     public function onAutoload($cls) {
122         $dir = dirname(__FILE__);
123
124         switch ($cls) {
125             case 'IrcManager':
126                 include_once $dir . '/'.strtolower($cls).'.php';
127                 return false;
128             case 'Fake_Irc':
129             case 'Irc_waiting_message':
130             case 'ChannelResponseChannel':
131                 include_once $dir . '/'. $cls .'.php';
132                 return false;
133             default:
134                 if (substr($cls, 0, 7) == 'Phergie') {
135                     include_once str_replace('_', DIRECTORY_SEPARATOR, $cls) . '.php';
136                     return false;
137                 }
138                 return true;
139         }
140     }
141
142     /*
143      * Start manager on daemon start
144      *
145      * @param array &$versions Array to insert manager into
146      * @return boolean
147      */
148     public function onStartImDaemonIoManagers(&$classes) {
149         parent::onStartImDaemonIoManagers(&$classes);
150         $classes[] = new IrcManager($this); // handles sending/receiving
151         return true;
152     }
153
154     /**
155     * Ensure the database table is present
156     *
157     */
158     public function onCheckSchema() {
159         $schema = Schema::get();
160
161         // For storing messages while sessions become ready
162         $schema->ensureTable('irc_waiting_message',
163                              array(new ColumnDef('id', 'integer', null,
164                                                  false, 'PRI', null, null, true),
165                                    new ColumnDef('data', 'blob', null, false),
166                                    new ColumnDef('prioritise', 'tinyint', 1, false),
167                                    new ColumnDef('created', 'datetime', null, false),
168                                    new ColumnDef('claimed', 'datetime')));
169
170         return true;
171     }
172
173     /**
174     * Get a microid URI for the given screenname
175     *
176     * @param string $screenname Screenname
177     * @return string microid URI
178     */
179     public function microiduri($screenname) {
180         return 'irc:' . $screenname;
181     }
182
183     /**
184      * Send a message to a given screenname
185      *
186      * @param string $screenname Screenname to send to
187      * @param string $body Text to send
188      * @return boolean true on success
189      */
190     public function send_message($screenname, $body) {
191         $lines = explode("\n", $body);
192         foreach ($lines as $line) {
193             $this->fake_irc->doPrivmsg($screenname, $line);
194             $this->enqueue_outgoing_raw(array('type' => 'message', 'prioritise' => 0, 'data' => $this->fake_irc->would_be_sent));
195         }
196         return true;
197     }
198
199     /**
200      * Accept a queued input message.
201      *
202      * @return boolean true if processing completed, false if message should be reprocessed
203      */
204     public function receive_raw_message($data) {
205         if (strpos($data['source'], '#') === 0) {
206             $message = $data['message'];
207             $parts = explode(' ', $message, 2);
208             $command = $parts[0];
209             if (in_array($command, $this->whiteList)) {
210                 $this->handle_channel_incoming($data['sender'], $data['source'], $message);
211             } else {
212                 $this->handle_incoming($data['sender'], $message);
213             }
214         } else {
215             $this->handle_incoming($data['sender'], $data['message']);
216         }
217         return true;
218     }
219
220     /**
221      * Helper for handling incoming messages from a channel requiring response
222      * to the channel instead of via PM
223      *
224      * @param string $nick Screenname the message was sent from
225      * @param string $channel Channel the message originated from
226      * @param string $message Message text
227      * @param boolean true on success
228      */
229     protected function handle_channel_incoming($nick, $channel, $notice_text) {
230         $user = $this->get_user($nick);
231         // For common_current_user to work
232         global $_cur;
233         $_cur = $user;
234
235         if (!$user) {
236             $this->send_from_site($nick, 'Unknown user; go to ' .
237                              common_local_url('imsettings') .
238                              ' to add your address to your account');
239             common_log(LOG_WARNING, 'Message from unknown user ' . $nick);
240             return;
241         }
242         if ($this->handle_channel_command($user, $channel, $notice_text)) {
243             common_log(LOG_INFO, "Command message by $nick handled.");
244             return;
245         } else if ($this->is_autoreply($notice_text)) {
246             common_log(LOG_INFO, 'Ignoring auto reply from ' . $nick);
247             return;
248         } else if ($this->is_otr($notice_text)) {
249             common_log(LOG_INFO, 'Ignoring OTR from ' . $nick);
250             return;
251         } else {
252             common_log(LOG_INFO, 'Posting a notice from ' . $user->nickname);
253             $this->add_notice($nick, $user, $notice_text);
254         }
255
256         $user->free();
257         unset($user);
258         unset($_cur);
259         unset($message);
260     }
261
262     /**
263      * Attempt to handle a message from a channel as a command
264      *
265      * @param User $user User the message is from
266      * @param string $channel Channel the message originated from
267      * @param string $body Message text
268      * @return boolean true if the message was a command and was executed, false if it was not a command
269      */
270     protected function handle_channel_command($user, $channel, $body) {
271         $inter = new CommandInterpreter();
272         $cmd = $inter->handle_command($user, $body);
273         if ($cmd) {
274             $chan = new ChannelResponseChannel($this, $channel);
275             $cmd->execute($chan);
276             return true;
277         } else {
278             return false;
279         }
280     }
281
282     /**
283      * Send a confirmation code to a user
284      *
285      * @param string $screenname screenname sending to
286      * @param string $code the confirmation code
287      * @param User $user user sending to
288      * @return boolean success value
289      */
290     public function send_confirmation_code($screenname, $code, $user, $checked = false) {
291         $body = sprintf(_('User "%s" on %s has said that your %s screenname belongs to them. ' .
292           'If that\'s true, you can confirm by clicking on this URL: ' .
293           '%s' .
294           ' . (If you cannot click it, copy-and-paste it into the ' .
295           'address bar of your browser). If that user isn\'t you, ' .
296           'or if you didn\'t request this confirmation, just ignore this message.'),
297           $user->nickname, common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', array('code' => $code)));
298
299         if ($this->regcheck && !$checked) {
300             return $this->checked_send_confirmation_code($screenname, $code, $user);
301         } else {
302             return $this->send_message($screenname, $body);
303         }
304     }
305
306     /**
307     * Only sends the confirmation message if the nick is
308     * registered
309     *
310     * @param string $screenname Screenname sending to
311     * @param string $code The confirmation code
312     * @param User $user User sending to
313     * @return boolean true on succes
314     */
315     public function checked_send_confirmation_code($screenname, $code, $user) {
316         $this->fake_irc->doPrivmsg('NickServ', 'INFO '.$screenname);
317         $this->enqueue_outgoing_raw(
318             array(
319                 'type' => 'nickcheck',
320                 'prioritise' => 1,
321                 'data' => $this->fake_irc->would_be_sent,
322                 'nickdata' =>
323                     array(
324                         'screenname' => $screenname,
325                         'code' => $code,
326                         'user' => $user
327                     )
328             )
329         );
330         return true;
331     }
332
333     /**
334     * Initialize plugin
335     *
336     * @return boolean
337     */
338     public function initialize() {
339         if (!isset($this->host)) {
340             throw new Exception('must specify a host');
341         }
342         if (!isset($this->username)) {
343             throw new Exception('must specify a username');
344         }
345         if (!isset($this->realname)) {
346             throw new Exception('must specify a "real name"');
347         }
348         if (!isset($this->nick)) {
349             throw new Exception('must specify a nickname');
350         }
351
352         if (!isset($this->port)) {
353             $this->port = 6667;
354         }
355         if (!isset($this->transporttype)) {
356             $this->transporttype = 'tcp';
357         }
358         if (!isset($this->encoding)) {
359             $this->encoding = 'UTF-8';
360         }
361
362         if (!isset($this->regcheck)) {
363             $this->regcheck = true;
364         }
365
366         $this->fake_irc = new Fake_Irc;
367
368         /*
369          * Commands allowed to return output to a channel
370          */
371         $this->whiteList = array('stats', 'last', 'get');
372
373         return true;
374     }
375
376     /**
377      * Get plugin information
378      *
379      * @param array $versions Array to insert information into
380      * @return void
381      */
382     public function onPluginVersion(&$versions) {
383         $versions[] = array('name' => 'IRC',
384                             'version' => STATUSNET_VERSION,
385                             'author' => 'Luke Fitzgerald',
386                             'homepage' => 'http://status.net/wiki/Plugin:IRC',
387                             'rawdescription' =>
388                             _m('The IRC plugin allows users to send and receive notices over an IRC network.'));
389         return true;
390     }
391 }