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