]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Blacklist/BlacklistPlugin.php
Update translator documentation.
[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(sprintf(_m('Unknown data type for config %1$s + %2$s.'),$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                 // TRANS: Validation failure for URL. %s is the URL.
149                 $msg = sprintf(_m("You may not register with homepage \"%s\"."),
150                                $homepage);
151                 throw new ClientException($msg);
152             }
153         }
154
155         $nickname = strtolower($action->trimmed('nickname'));
156
157         if (!empty($nickname)) {
158             if (!$this->_checkNickname($nickname)) {
159                 // TRANS: Validation failure for nickname. %s is the nickname.
160                 $msg = sprintf(_m("You may not register with nickname \"%s\"."),
161                                $nickname);
162                 throw new ClientException($msg);
163             }
164         }
165
166         return true;
167     }
168
169     /**
170      * Hook profile update to prevent blacklisted homepages or nicknames
171      *
172      * Throws an exception if there's a blacklisted homepage or nickname.
173      *
174      * @param Action $action Action being called (usually register)
175      *
176      * @return boolean hook value
177      */
178     function onStartProfileSaveForm($action)
179     {
180         $homepage = strtolower($action->trimmed('homepage'));
181
182         if (!empty($homepage)) {
183             if (!$this->_checkUrl($homepage)) {
184                 // TRANS: Validation failure for URL. %s is the URL.
185                 $msg = sprintf(_m("You may not use homepage \"%s\"."),
186                                $homepage);
187                 throw new ClientException($msg);
188             }
189         }
190
191         $nickname = strtolower($action->trimmed('nickname'));
192
193         if (!empty($nickname)) {
194             if (!$this->_checkNickname($nickname)) {
195                 // TRANS: Validation failure for nickname. %s is the nickname.
196                 $msg = sprintf(_m("You may not use nickname \"%s\"."),
197                                $nickname);
198                 throw new ClientException($msg);
199             }
200         }
201
202         return true;
203     }
204
205     /**
206      * Hook notice save to prevent blacklisted urls
207      *
208      * Throws an exception if there's a blacklisted url in the content.
209      *
210      * @param Notice &$notice Notice being saved
211      *
212      * @return boolean hook value
213      */
214     function onStartNoticeSave(&$notice)
215     {
216         common_replace_urls_callback($notice->content,
217                                      array($this, 'checkNoticeUrl'));
218         return true;
219     }
220
221     /**
222      * Helper callback for notice save
223      *
224      * Throws an exception if there's a blacklisted url in the content.
225      *
226      * @param string $url URL in the notice content
227      *
228      * @return boolean hook value
229      */
230     function checkNoticeUrl($url)
231     {
232         // It comes in special'd, so we unspecial it
233         // before comparing against patterns
234
235         $url = htmlspecialchars_decode($url);
236
237         if (!$this->_checkUrl($url)) {
238             // TRANS: Validation failure for URL. %s is the URL.
239             $msg = sprintf(_m("You may not use URL \"%s\" in notices."),
240                            $url);
241             throw new ClientException($msg);
242         }
243
244         return $url;
245     }
246
247     /**
248      * Helper for checking URLs
249      *
250      * Checks an URL against our patterns for a match.
251      *
252      * @param string $url URL to check
253      *
254      * @return boolean true means it's OK, false means it's bad
255      */
256     private function _checkUrl($url)
257     {
258         $patterns = $this->_getUrlPatterns();
259
260         foreach ($patterns as $pattern) {
261             if ($pattern != '' && preg_match("/$pattern/", $url)) {
262                 return false;
263             }
264         }
265
266         return true;
267     }
268
269     /**
270      * Helper for checking nicknames
271      *
272      * Checks a nickname against our patterns for a match.
273      *
274      * @param string $nickname nickname to check
275      *
276      * @return boolean true means it's OK, false means it's bad
277      */
278     private function _checkNickname($nickname)
279     {
280         $patterns = $this->_getNicknamePatterns();
281
282         foreach ($patterns as $pattern) {
283             if ($pattern != '' && preg_match("/$pattern/", $nickname)) {
284                 return false;
285             }
286         }
287
288         return true;
289     }
290
291     /**
292      * Add our actions to the URL router
293      *
294      * @param Net_URL_Mapper $m URL mapper for this hit
295      *
296      * @return boolean hook return
297      */
298     function onRouterInitialized($m)
299     {
300         $m->connect('panel/blacklist', array('action' => 'blacklistadminpanel'));
301         return true;
302     }
303
304     /**
305      * Auto-load our classes if called
306      *
307      * @param string $cls Class to load
308      *
309      * @return boolean hook return
310      */
311     function onAutoload($cls)
312     {
313         switch (strtolower($cls))
314         {
315         case 'nickname_blacklist':
316         case 'homepage_blacklist':
317             include_once INSTALLDIR.'/plugins/Blacklist/'.ucfirst($cls).'.php';
318             return false;
319         case 'blacklistadminpanelaction':
320             $base = strtolower(mb_substr($cls, 0, -6));
321             include_once INSTALLDIR.'/plugins/Blacklist/'.$base.'.php';
322             return false;
323         default:
324             return true;
325         }
326     }
327
328     /**
329      * Plugin version data
330      *
331      * @param array &$versions array of version blocks
332      *
333      * @return boolean hook value
334      */
335     function onPluginVersion(&$versions)
336     {
337         $versions[] = array('name' => 'Blacklist',
338                             'version' => self::VERSION,
339                             'author' => 'Evan Prodromou',
340                             'homepage' =>
341                             'http://status.net/wiki/Plugin:Blacklist',
342                             'description' =>
343                             // TRANS: Plugin description.
344                             _m('Keeps a blacklist of forbidden nickname '.
345                                'and URL patterns.'));
346         return true;
347     }
348
349     /**
350      * Determines if our admin panel can be shown
351      *
352      * @param string  $name  name of the admin panel
353      * @param boolean &$isOK result
354      *
355      * @return boolean hook value
356      */
357     function onAdminPanelCheck($name, &$isOK)
358     {
359         if ($name == 'blacklist') {
360             $isOK = $this->canAdmin;
361             return false;
362         }
363
364         return true;
365     }
366
367     /**
368      * Add our tab to the admin panel
369      *
370      * @param Widget $nav Admin panel nav
371      *
372      * @return boolean hook value
373      */
374     function onEndAdminPanelNav($nav)
375     {
376         if (AdminPanelAction::canAdmin('blacklist')) {
377
378             $action_name = $nav->action->trimmed('action');
379
380             $nav->out->menuItem(common_local_url('blacklistadminpanel'),
381                                 // TRANS: Menu item in admin panel.
382                                 _m('MENU','Blacklist'),
383                                 // TRANS: Tooltip for menu item in admin panel.
384                                 _m('TOOLTIP','Blacklist configuration.'),
385                                 $action_name == 'blacklistadminpanel',
386                                 'nav_blacklist_admin_panel');
387         }
388
389         return true;
390     }
391
392     function onEndDeleteUserForm($action, $user)
393     {
394         $cur = common_current_user();
395
396         if (empty($cur) || !$cur->hasRight(Right::CONFIGURESITE)) {
397             return;
398         }
399
400         $profile = $user->getProfile();
401
402         if (empty($profile)) {
403             return;
404         }
405
406         $action->elementStart('ul', 'form_data');
407         $action->elementStart('li');
408         $this->checkboxAndText($action,
409                                'blacklistnickname',
410                                // TRANS: Checkbox label in the blacklist user form.
411                                _m('Add this nickname pattern to blacklist'),
412                                'blacklistnicknamepattern',
413                                $this->patternizeNickname($user->nickname));
414         $action->elementEnd('li');
415
416         if (!empty($profile->homepage)) {
417             $action->elementStart('li');
418             $this->checkboxAndText($action,
419                                    'blacklisthomepage',
420                                    // TRANS: Checkbox label in the blacklist user form.
421                                    _m('Add this homepage pattern to blacklist'),
422                                    'blacklisthomepagepattern',
423                                    $this->patternizeHomepage($profile->homepage));
424             $action->elementEnd('li');
425         }
426
427         $action->elementEnd('ul');
428     }
429
430     function onEndDeleteUser($action, $user)
431     {
432         if ($action->boolean('blacklisthomepage')) {
433             $pattern = $action->trimmed('blacklisthomepagepattern');
434             Homepage_blacklist::ensurePattern($pattern);
435         }
436
437         if ($action->boolean('blacklistnickname')) {
438             $pattern = $action->trimmed('blacklistnicknamepattern');
439             Nickname_blacklist::ensurePattern($pattern);
440         }
441
442         return true;
443     }
444
445     function checkboxAndText($action, $checkID, $label, $textID, $value)
446     {
447         $action->element('input', array('name' => $checkID,
448                                         'type' => 'checkbox',
449                                         'class' => 'checkbox',
450                                         'id' => $checkID));
451
452         $action->text(' ');
453
454         $action->element('label', array('class' => 'checkbox',
455                                         'for' => $checkID),
456                          $label);
457
458         $action->text(' ');
459
460         $action->element('input', array('name' => $textID,
461                                         'type' => 'text',
462                                         'id' => $textID,
463                                         'value' => $value));
464     }
465
466     function patternizeNickname($nickname)
467     {
468         return $nickname;
469     }
470
471     function patternizeHomepage($homepage)
472     {
473         $hostname = parse_url($homepage, PHP_URL_HOST);
474         return $hostname;
475     }
476
477     function onStartHandleFeedEntry($activity)
478     {
479         return $this->_checkActivity($activity);
480     }
481
482     function onStartHandleSalmon($activity)
483     {
484         return $this->_checkActivity($activity);
485     }
486
487     function _checkActivity($activity)
488     {
489         $actor = $activity->actor;
490
491         if (empty($actor)) {
492             return true;
493         }
494
495         $homepage = strtolower($actor->link);
496
497         if (!empty($homepage)) {
498             if (!$this->_checkUrl($homepage)) {
499                 // TRANS: Exception thrown trying to post a notice while having set a blocked homepage URL. %s is the blocked URL.
500                 $msg = sprintf(_m("Users from \"%s\" are blocked."),
501                                $homepage);
502                 throw new ClientException($msg);
503             }
504         }
505
506         $nickname = strtolower($actor->poco->preferredUsername);
507
508         if (!empty($nickname)) {
509             if (!$this->_checkNickname($nickname)) {
510                 // TRANS: Exception thrown trying to post a notice while having a blocked nickname. %s is the blocked nickname.
511                 $msg = sprintf(_m("Notices from nickname \"%s\" disallowed."),
512                                $nickname);
513                 throw new ClientException($msg);
514             }
515         }
516
517         return true;
518     }
519
520     /**
521      * Check URLs and homepages for blacklisted users.
522      */
523     function onStartSubscribe($subscriber, $other)
524     {
525         foreach (array($other->profileurl, $other->homepage) as $url) {
526
527             if (empty($url)) {
528                 continue;
529             }
530
531             $url = strtolower($url);
532
533             if (!$this->_checkUrl($url)) {
534                 // TRANS: Client exception thrown trying to subscribe to a person with a blocked homepage or site URL. %s is the blocked URL.
535                 $msg = sprintf(_m("Users from \"%s\" are blocked."),
536                                $url);
537                 throw new ClientException($msg);
538             }
539         }
540
541         $nickname = $other->nickname;
542
543         if (!empty($nickname)) {
544             if (!$this->_checkNickname($nickname)) {
545                 // TRANS: Client exception thrown trying to subscribe to a person with a blocked nickname. %s is the blocked nickname.
546                 $msg = sprintf(_m("Cannot subscribe to nickname \"%s\"."),
547                                $nickname);
548                 throw new ClientException($msg);
549             }
550         }
551
552         return true;
553     }
554 }