]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Blacklist/BlacklistPlugin.php
Merge branch 'master' into testing
[quix0rs-gnu-social.git] / plugins / Blacklist / BlacklistPlugin.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Plugin to prevent use of nicknames or URLs on a blacklist
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  Action
23  * @package   StatusNet
24  * @author    Evan Prodromou <evan@status.net>
25  * @copyright 2010 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
30 if (!defined('STATUSNET')) {
31     exit(1);
32 }
33
34 /**
35  * Plugin to prevent use of nicknames or URLs on a blacklist
36  *
37  * @category Plugin
38  * @package  StatusNet
39  * @author   Evan Prodromou <evan@status.net>
40  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
41  * @link     http://status.net/
42  */
43
44 class BlacklistPlugin extends Plugin
45 {
46     const VERSION = STATUSNET_VERSION;
47
48     public $nicknames = array();
49     public $urls      = array();
50     public $canAdmin  = true;
51
52     function _getNicknamePatterns()
53     {
54         $confNicknames = $this->_configArray('blacklist', 'nicknames');
55
56         $dbNicknames = Nickname_blacklist::getPatterns();
57
58         return array_merge($this->nicknames,
59                            $confNicknames,
60                            $dbNicknames);
61     }
62
63     function _getUrlPatterns()
64     {
65         $confURLs = $this->_configArray('blacklist', 'urls');
66
67         $dbURLs = Homepage_blacklist::getPatterns();
68
69         return array_merge($this->urls,
70                            $confURLs,
71                            $dbURLs);
72     }
73
74     /**
75      * Database schema setup
76      *
77      * @return boolean hook value
78      */
79
80     function onCheckSchema()
81     {
82         $schema = Schema::get();
83
84         // For storing blacklist patterns for nicknames
85
86         $schema->ensureTable('nickname_blacklist',
87                              array(new ColumnDef('pattern',
88                                                  'varchar',
89                                                  255,
90                                                  false,
91                                                  'PRI'),
92                                    new ColumnDef('created',
93                                                  'datetime',
94                                                  null,
95                                                  false)));
96
97         $schema->ensureTable('homepage_blacklist',
98                              array(new ColumnDef('pattern',
99                                                  'varchar',
100                                                  255,
101                                                  false,
102                                                  'PRI'),
103                                    new ColumnDef('created',
104                                                  'datetime',
105                                                  null,
106                                                  false)));
107
108         return true;
109     }
110
111     /**
112      * Retrieve an array from configuration
113      *
114      * Carefully checks a section.
115      *
116      * @param string $section Configuration section
117      * @param string $setting Configuration setting
118      *
119      * @return array configuration values
120      */
121
122     function _configArray($section, $setting)
123     {
124         $config = common_config($section, $setting);
125
126         if (empty($config)) {
127             return array();
128         } else if (is_array($config)) {
129             return $config;
130         } else if (is_string($config)) {
131             return explode("\r\n", $config);
132         } else {
133             throw new Exception("Unknown data type for config $section + $setting");
134         }
135     }
136
137     /**
138      * Hook registration to prevent blacklisted homepages or nicknames
139      *
140      * Throws an exception if there's a blacklisted homepage or nickname.
141      *
142      * @param Action $action Action being called (usually register)
143      *
144      * @return boolean hook value
145      */
146
147     function onStartRegistrationTry($action)
148     {
149         $homepage = strtolower($action->trimmed('homepage'));
150
151         if (!empty($homepage)) {
152             if (!$this->_checkUrl($homepage)) {
153                 $msg = sprintf(_m("You may not register with homepage '%s'"),
154                                $homepage);
155                 throw new ClientException($msg);
156             }
157         }
158
159         $nickname = strtolower($action->trimmed('nickname'));
160
161         if (!empty($nickname)) {
162             if (!$this->_checkNickname($nickname)) {
163                 $msg = sprintf(_m("You may not register with nickname '%s'"),
164                                $nickname);
165                 throw new ClientException($msg);
166             }
167         }
168
169         return true;
170     }
171
172     /**
173      * Hook profile update to prevent blacklisted homepages or nicknames
174      *
175      * Throws an exception if there's a blacklisted homepage or nickname.
176      *
177      * @param Action $action Action being called (usually register)
178      *
179      * @return boolean hook value
180      */
181
182     function onStartProfileSaveForm($action)
183     {
184         $homepage = strtolower($action->trimmed('homepage'));
185
186         if (!empty($homepage)) {
187             if (!$this->_checkUrl($homepage)) {
188                 $msg = sprintf(_m("You may not use homepage '%s'"),
189                                $homepage);
190                 throw new ClientException($msg);
191             }
192         }
193
194         $nickname = strtolower($action->trimmed('nickname'));
195
196         if (!empty($nickname)) {
197             if (!$this->_checkNickname($nickname)) {
198                 $msg = sprintf(_m("You may not use nickname '%s'"),
199                                $nickname);
200                 throw new ClientException($msg);
201             }
202         }
203
204         return true;
205     }
206
207     /**
208      * Hook notice save to prevent blacklisted urls
209      *
210      * Throws an exception if there's a blacklisted url in the content.
211      *
212      * @param Notice &$notice Notice being saved
213      *
214      * @return boolean hook value
215      */
216
217     function onStartNoticeSave(&$notice)
218     {
219         common_replace_urls_callback($notice->content,
220                                      array($this, 'checkNoticeUrl'));
221         return true;
222     }
223
224     /**
225      * Helper callback for notice save
226      *
227      * Throws an exception if there's a blacklisted url in the content.
228      *
229      * @param string $url URL in the notice content
230      *
231      * @return boolean hook value
232      */
233
234     function checkNoticeUrl($url)
235     {
236         // It comes in special'd, so we unspecial it
237         // before comparing against patterns
238
239         $url = htmlspecialchars_decode($url);
240
241         if (!$this->_checkUrl($url)) {
242             $msg = sprintf(_m("You may not use url '%s' in notices"),
243                            $url);
244             throw new ClientException($msg);
245         }
246
247         return $url;
248     }
249
250     /**
251      * Helper for checking URLs
252      *
253      * Checks an URL against our patterns for a match.
254      *
255      * @param string $url URL to check
256      *
257      * @return boolean true means it's OK, false means it's bad
258      */
259
260     private function _checkUrl($url)
261     {
262         $patterns = $this->_getUrlPatterns();
263
264         foreach ($patterns as $pattern) {
265             if (preg_match("/$pattern/", $url)) {
266                 return false;
267             }
268         }
269
270         return true;
271     }
272
273     /**
274      * Helper for checking nicknames
275      *
276      * Checks a nickname against our patterns for a match.
277      *
278      * @param string $nickname nickname to check
279      *
280      * @return boolean true means it's OK, false means it's bad
281      */
282
283     private function _checkNickname($nickname)
284     {
285         $patterns = $this->_getNicknamePatterns();
286
287         foreach ($patterns as $pattern) {
288             if (preg_match("/$pattern/", $nickname)) {
289                 return false;
290             }
291         }
292
293         return true;
294     }
295
296     /**
297      * Add our actions to the URL router
298      *
299      * @param Net_URL_Mapper $m URL mapper for this hit
300      *
301      * @return boolean hook return
302      */
303
304     function onRouterInitialized($m)
305     {
306         $m->connect('admin/blacklist', array('action' => 'blacklistadminpanel'));
307         return true;
308     }
309
310     /**
311      * Auto-load our classes if called
312      *
313      * @param string $cls Class to load
314      *
315      * @return boolean hook return
316      */
317
318     function onAutoload($cls)
319     {
320         switch (strtolower($cls))
321         {
322         case 'nickname_blacklist':
323         case 'homepage_blacklist':
324             include_once INSTALLDIR.'/plugins/Blacklist/'.ucfirst($cls).'.php';
325             return false;
326         case 'blacklistadminpanelaction':
327             $base = strtolower(mb_substr($cls, 0, -6));
328             include_once INSTALLDIR.'/plugins/Blacklist/'.$base.'.php';
329             return false;
330         default:
331             return true;
332         }
333     }
334
335     /**
336      * Plugin version data
337      *
338      * @param array &$versions array of version blocks
339      *
340      * @return boolean hook value
341      */
342
343     function onPluginVersion(&$versions)
344     {
345         $versions[] = array('name' => 'Blacklist',
346                             'version' => self::VERSION,
347                             'author' => 'Evan Prodromou',
348                             'homepage' =>
349                             'http://status.net/wiki/Plugin:Blacklist',
350                             'description' =>
351                             _m('Keep a blacklist of forbidden nickname '.
352                                'and URL patterns.'));
353         return true;
354     }
355
356     /**
357      * Determines if our admin panel can be shown
358      *
359      * @param string  $name  name of the admin panel
360      * @param boolean &$isOK result
361      *
362      * @return boolean hook value
363      */
364
365     function onAdminPanelCheck($name, &$isOK)
366     {
367         if ($name == 'blacklist') {
368             $isOK = $this->canAdmin;
369             return false;
370         }
371
372         return true;
373     }
374
375     /**
376      * Add our tab to the admin panel
377      *
378      * @param Widget $nav Admin panel nav
379      *
380      * @return boolean hook value
381      */
382
383     function onEndAdminPanelNav($nav)
384     {
385         if (AdminPanelAction::canAdmin('blacklist')) {
386
387             $action_name = $nav->action->trimmed('action');
388
389             $nav->out->menuItem(common_local_url('blacklistadminpanel'),
390                                 _('Blacklist'),
391                                 _('Blacklist configuration'),
392                                 $action_name == 'blacklistadminpanel',
393                                 'nav_blacklist_admin_panel');
394         }
395
396         return true;
397     }
398
399     function onEndDeleteUserForm($action, $user)
400     {
401         $cur = common_current_user();
402
403         if (empty($cur) || !$cur->hasRight(Right::CONFIGURESITE)) {
404             return;
405         }
406
407         $profile = $user->getProfile();
408
409         if (empty($profile)) {
410             return;
411         }
412
413         $action->elementStart('ul', 'form_data');
414         $action->elementStart('li');
415         $this->checkboxAndText($action,
416                                'blacklistnickname',
417                                _('Add this nickname pattern to blacklist'),
418                                'blacklistnicknamepattern',
419                                $this->patternizeNickname($user->nickname));
420         $action->elementEnd('li');
421
422         if (!empty($profile->homepage)) {
423             $action->elementStart('li');
424             $this->checkboxAndText($action,
425                                    'blacklisthomepage',
426                                    _('Add this homepage pattern to blacklist'),
427                                    'blacklisthomepagepattern',
428                                    $this->patternizeHomepage($profile->homepage));
429             $action->elementEnd('li');
430         }
431
432         $action->elementEnd('ul');
433     }
434
435     function onEndDeleteUser($action, $user)
436     {
437         if ($action->boolean('blacklisthomepage')) {
438             $pattern = $action->trimmed('blacklisthomepagepattern');
439             Homepage_blacklist::ensurePattern($pattern);
440         }
441
442         if ($action->boolean('blacklistnickname')) {
443             $pattern = $action->trimmed('blacklistnicknamepattern');
444             Nickname_blacklist::ensurePattern($pattern);
445         }
446
447         return true;
448     }
449
450     function checkboxAndText($action, $checkID, $label, $textID, $value)
451     {
452         $action->element('input', array('name' => $checkID,
453                                         'type' => 'checkbox',
454                                         'class' => 'checkbox',
455                                         'id' => $checkID));
456
457         $action->text(' ');
458
459         $action->element('label', array('class' => 'checkbox',
460                                         'for' => $checkID),
461                          $label);
462
463         $action->text(' ');
464
465         $action->element('input', array('name' => $textID,
466                                         'type' => 'text',
467                                         'id' => $textID,
468                                         'value' => $value));
469     }
470
471     function patternizeNickname($nickname)
472     {
473         return $nickname;
474     }
475
476     function patternizeHomepage($homepage)
477     {
478         $hostname = parse_url($homepage, PHP_URL_HOST);
479         return $hostname;
480     }
481 }