]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Irc/IrcPlugin.php
More info for a proper, fancy-url lighttpd setup
[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 class IrcPlugin extends ImPlugin {
51     public $host =  null;
52     public $port = null;
53     public $username = null;
54     public $realname = null;
55     public $nick = null;
56     public $password = null;
57     public $nickservidentifyregexp = null;
58     public $nickservpassword = null;
59     public $channels = null;
60     public $transporttype = null;
61     public $encoding = null;
62     public $pinginterval = 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         // TRANS: Service name for IRC.
79         return _m('IRC');
80     }
81
82     /**
83      * Normalize a screenname for comparison
84      *
85      * @param string $screenname Screenname to normalize
86      * @return string An equivalent screenname in normalized form
87      */
88     public function normalize($screenname) {
89         $screenname = str_replace(" ","", $screenname);
90         return strtolower($screenname);
91     }
92
93     /**
94      * Get the screenname of the daemon that sends and receives messages
95      *
96      * @return string Screenname
97      */
98     public function daemonScreenname() {
99         return $this->nick;
100     }
101
102     /**
103      * Validate (ensure the validity of) a screenname
104      *
105      * @param string $screenname Screenname to validate
106      * @return boolean true if screenname is valid
107      */
108     public function validate($screenname) {
109         if (preg_match('/\A[a-z0-9\-_]{1,1000}\z/i', $screenname)) {
110             return true;
111         } else {
112             return false;
113         }
114     }
115
116     /**
117      * Load related modules when needed
118      *
119      * @param string $cls Name of the class to be loaded
120      * @return boolean hook value; true means continue processing, false means stop.
121      */
122     public function onAutoload($cls) {
123         $dir = dirname(__FILE__);
124
125         switch ($cls) {
126             case 'IrcManager':
127                 include_once $dir . '/'.strtolower($cls).'.php';
128                 return false;
129             case 'Fake_Irc':
130             case 'Irc_waiting_message':
131             case 'ChannelResponseChannel':
132                 include_once $dir . '/'. $cls .'.php';
133                 return false;
134             default:
135                 if (substr($cls, 0, 7) == 'Phergie') {
136                     include_once str_replace('_', DIRECTORY_SEPARATOR, $cls) . '.php';
137                     return false;
138                 }
139                 return true;
140         }
141     }
142
143     /*
144      * Start manager on daemon start
145      *
146      * @param array &$versions Array to insert manager into
147      * @return boolean
148      */
149     public function onStartImDaemonIoManagers(&$classes) {
150         parent::onStartImDaemonIoManagers($classes);
151         $classes[] = new IrcManager($this); // handles sending/receiving
152         return true;
153     }
154
155     /**
156     * Ensure the database table is present
157     *
158     */
159     public function onCheckSchema() {
160         $schema = Schema::get();
161
162         // For storing messages while sessions become ready
163         $schema->ensureTable('irc_waiting_message', Irc_waiting_message::schemaDef());
164         return true;
165     }
166
167     /**
168     * Get a microid URI for the given screenname
169     *
170     * @param string $screenname Screenname
171     * @return string microid URI
172     */
173     public function microiduri($screenname) {
174         return 'irc:' . $screenname;
175     }
176
177     /**
178      * Send a message to a given screenname
179      *
180      * @param string $screenname Screenname to send to
181      * @param string $body Text to send
182      * @return boolean true on success
183      */
184     public function sendMessage($screenname, $body) {
185         $lines = explode("\n", $body);
186         foreach ($lines as $line) {
187             $this->fake_irc->doPrivmsg($screenname, $line);
188             $this->enqueueOutgoingRaw(array('type' => 'message', 'prioritise' => 0, 'data' => $this->fake_irc->would_be_sent));
189         }
190         return true;
191     }
192
193     /**
194      * Accept a queued input message.
195      *
196      * @return boolean true if processing completed, false if message should be reprocessed
197      */
198     public function receiveRawMessage($data) {
199         if (strpos($data['source'], '#') === 0) {
200             $message = $data['message'];
201             $parts = explode(' ', $message, 2);
202             $command = $parts[0];
203             if (in_array($command, $this->whiteList)) {
204                 $this->handle_channel_incoming($data['sender'], $data['source'], $message);
205             } else {
206                 $this->handleIncoming($data['sender'], $message);
207             }
208         } else {
209             $this->handleIncoming($data['sender'], $data['message']);
210         }
211         return true;
212     }
213
214     /**
215      * Helper for handling incoming messages from a channel requiring response
216      * to the channel instead of via PM
217      *
218      * @param string $nick Screenname the message was sent from
219      * @param string $channel Channel the message originated from
220      * @param string $message Message text
221      * @param boolean true on success
222      */
223     protected function handle_channel_incoming($nick, $channel, $notice_text) {
224         $user = $this->getUser($nick);
225         // For common_current_user to work
226         global $_cur;
227         $_cur = $user;
228
229         if (!$user) {
230             $this->sendFromSite($nick, 'Unknown user; go to ' .
231                              common_local_url('imsettings') .
232                              ' to add your address to your account');
233             common_log(LOG_WARNING, 'Message from unknown user ' . $nick);
234             return;
235         }
236         if ($this->handle_channel_command($user, $channel, $notice_text)) {
237             common_log(LOG_INFO, "Command message by $nick handled.");
238             return;
239         } else if ($this->isAutoreply($notice_text)) {
240             common_log(LOG_INFO, 'Ignoring auto reply from ' . $nick);
241             return;
242         } else if ($this->isOtr($notice_text)) {
243             common_log(LOG_INFO, 'Ignoring OTR from ' . $nick);
244             return;
245         } else {
246             common_log(LOG_INFO, 'Posting a notice from ' . $user->nickname);
247             $this->addNotice($nick, $user, $notice_text);
248         }
249
250         $user->free();
251         unset($user);
252         unset($_cur);
253         unset($message);
254     }
255
256     /**
257      * Attempt to handle a message from a channel as a command
258      *
259      * @param User $user User the message is from
260      * @param string $channel Channel the message originated from
261      * @param string $body Message text
262      * @return boolean true if the message was a command and was executed, false if it was not a command
263      */
264     protected function handle_channel_command($user, $channel, $body) {
265         $inter = new CommandInterpreter();
266         $cmd = $inter->handle_command($user, $body);
267         if ($cmd) {
268             $chan = new ChannelResponseChannel($this, $channel);
269             $cmd->execute($chan);
270             return true;
271         } else {
272             return false;
273         }
274     }
275
276     /**
277      * Send a confirmation code to a user
278      *
279      * @param string $screenname screenname sending to
280      * @param string $code the confirmation code
281      * @param User $user user sending to
282      * @return boolean success value
283      */
284     public function sendConfirmationCode($screenname, $code, $user, $checked = false) {
285         // TRANS: Body text for e-mail confirmation message for IRC.
286         // TRANS: %1$s is a user nickname, %2$s is the StatusNet sitename,
287         // TRANS: %3$s is the plugin display name ("IRC"), %4$s is the confirm address URL.
288         $body = sprintf(_m('User "%1$s" on %2$s has said that your %3$s screenname belongs to them. ' .
289           'If that\'s true, you can confirm by clicking on this URL: ' .
290           '%4$s' .
291           ' . (If you cannot click it, copy-and-paste it into the ' .
292           'address bar of your browser). If that user is not you, ' .
293           'or if you did not request this confirmation, just ignore this message.'),
294           $user->nickname, common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', array('code' => $code)));
295
296         if ($this->regcheck && !$checked) {
297             return $this->checked_sendConfirmationCode($screenname, $code, $user);
298         } else {
299             return $this->sendMessage($screenname, $body);
300         }
301     }
302
303     /**
304     * Only sends the confirmation message if the nick is
305     * registered
306     *
307     * @param string $screenname Screenname sending to
308     * @param string $code The confirmation code
309     * @param User $user User sending to
310     * @return boolean true on succes
311     */
312     public function checked_sendConfirmationCode($screenname, $code, $user) {
313         $this->fake_irc->doPrivmsg('NickServ', 'INFO '.$screenname);
314         $this->enqueueOutgoingRaw(
315             array(
316                 'type' => 'nickcheck',
317                 'prioritise' => 1,
318                 'data' => $this->fake_irc->would_be_sent,
319                 'nickdata' =>
320                     array(
321                         'screenname' => $screenname,
322                         'code' => $code,
323                         'user' => $user
324                     )
325             )
326         );
327         return true;
328     }
329
330     /**
331     * Initialize plugin
332     *
333     * @return boolean
334     */
335     public function initialize() {
336         if (!isset($this->host)) {
337             // TRANS: Exception thrown when initialising the IRC plugin fails because of an incorrect configuration.
338             throw new Exception(_m('You must specify a host.'));
339         }
340         if (!isset($this->username)) {
341             // TRANS: Exception thrown when initialising the IRC plugin fails because of an incorrect configuration.
342             throw new Exception(_m('You must specify a username.'));
343         }
344         if (!isset($this->realname)) {
345             // TRANS: Exception thrown when initialising the IRC plugin fails because of an incorrect configuration.
346             throw new Exception(_m('You must specify a "real name".'));
347         }
348         if (!isset($this->nick)) {
349             // TRANS: Exception thrown when initialising the IRC plugin fails because of an incorrect configuration.
350             throw new Exception(_m('You must specify a nickname.'));
351         }
352
353         if (!isset($this->port)) {
354             $this->port = 6667;
355         }
356         if (!isset($this->transporttype)) {
357             $this->transporttype = 'tcp';
358         }
359         if (!isset($this->encoding)) {
360             $this->encoding = 'UTF-8';
361         }
362         if (!isset($this->pinginterval)) {
363             $this->pinginterval = 120;
364         }
365
366         if (!isset($this->regcheck)) {
367             $this->regcheck = true;
368         }
369
370         $this->fake_irc = new Fake_Irc;
371
372         /*
373          * Commands allowed to return output to a channel
374          */
375         $this->whiteList = array('stats', 'last', 'get');
376
377         return true;
378     }
379
380     /**
381      * Get plugin information
382      *
383      * @param array $versions Array to insert information into
384      * @return void
385      */
386     public function onPluginVersion(&$versions) {
387         $versions[] = array('name' => 'IRC',
388                             'version' => STATUSNET_VERSION,
389                             'author' => 'Luke Fitzgerald',
390                             'homepage' => 'http://status.net/wiki/Plugin:IRC',
391                             'rawdescription' =>
392                             // TRANS: Plugin description.
393                             _m('The IRC plugin allows users to send and receive notices over an IRC network.'));
394         return true;
395     }
396 }