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