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