ca81ffb5961ba96c5c5f8e525900fe883aee5d9c
[friendica.git/.git] / src / Console / ServerBlock.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Console;
23
24 use Asika\SimpleConsole\CommandArgsException;
25 use Asika\SimpleConsole\Console;
26 use Console_Table;
27 use Friendica\Core\Config\IConfig;
28
29 /**
30  * Manage blocked servers
31  *
32  * With this tool, you can list the current blocked servers
33  * or you can add / remove a blocked server from the list
34  */
35 class ServerBlock extends Console
36 {
37         const DEFAULT_REASON = 'blocked';
38
39         protected $helpOptions = ['h', 'help', '?'];
40
41         /**
42          * @var IConfig
43          */
44         private $config;
45
46         protected function getHelp()
47         {
48                 $help = <<<HELP
49 console serverblock - Manage blocked server domain patterns
50 Usage
51     bin/console serverblock [-h|--help|-?] [-v]
52     bin/console serverblock add <pattern> <reason> [-h|--help|-?] [-v]
53     bin/console serverblock remove <pattern> [-h|--help|-?] [-v]
54     bin/console serverblock export <filename>
55     bin/console serverblock import <filename>
56
57 Description
58     With this tool, you can list the current blocked server domain patterns
59     or you can add / remove a blocked server domain pattern from the list.
60     Using the export and import options you can share your server blocklist
61     with other node admins by CSV files.
62
63     Patterns are case-insensitive shell wildcard comprising the following special characters:
64     - * : Any number of characters
65     - ? : Any single character
66     - [<char1><char2>...] : char1 or char2 or...
67
68 Options
69     -h|--help|-? Show help information
70     -v           Show more debug information.
71
72 HELP;
73                 return $help;
74         }
75
76         public function __construct(IConfig $config, $argv = null)
77         {
78                 parent::__construct($argv);
79
80                 $this->config = $config;
81         }
82
83         protected function doExecute()
84         {
85                 if (count($this->args) == 0) {
86                         $this->printBlockedServers($this->config);
87                         return 0;
88                 }
89
90                 switch ($this->getArgument(0)) {
91                         case 'add':
92                                 return $this->addBlockedServer($this->config);
93                         case 'remove':
94                                 return $this->removeBlockedServer($this->config);
95                         case 'export':
96                                 return $this->exportBlockedServers($this->config);
97                         case 'import':
98                                 return $this->importBlockedServers($this->config);
99                         default:
100                                 throw new CommandArgsException('Unknown command.');
101                                 break;
102                 }
103         }
104
105         /**
106          * Exports the list of blocked domains including the reason for the
107          * block to a CSV file.
108          *
109          * @param IConfig $config
110          */
111         private function exportBlockedServers(IConfig $config)
112         {
113                 $filename = $this->getArgument(1);
114                 $blocklist = $config->get('system', 'blocklist', []);
115                 $fp = fopen($filename, 'w');
116                 foreach ($blocklist as $domain) {
117                         fputcsv($fp, $domain);
118                 }
119         }
120         /**
121          * Imports a list of domains and a reason for the block from a CSV
122          * file, e.g. created with the export function.
123          *
124          * @param IConfig $config
125          */
126         private function importBlockedServers(IConfig $config)
127         {
128                 $filename = $this->getArgument(1);
129                 $currBlockList = $config->get('system', 'blocklist', []);
130                 $newBlockList = [];
131                 if (($fp = fopen($filename, 'r')) !== FALSE) {
132                         while (($data = fgetcsv($fp, 1000, ',')) !== FALSE) {
133                                 $domain = $data[0];
134                                 if (count($data)  == 0) {
135                                         $reason = self::DEFAULT_REASON;
136                                 } else {
137                                         $reason = $data[1];
138                                 }
139                                 $data = [
140                                         'domain' => $domain,
141                                         'reason' => $reason
142                                 ];
143                                 if (!in_array($data, $newBlockList))
144                                         $newBlockList[] = $data;
145                         }
146                         foreach ($currBlockList as $blocked) {
147                                 if (!in_array($blocked, $newBlockList))
148                                         $newBlockList[] = $blocked;
149                         }
150                         if ($config->set('system', 'blocklist', $newBlockList)) {
151                                 $this->out(sprintf("Entries from %s that were not blocked before are now blocked", $filename));
152                                 return 0;
153                         } else {
154                                 $this->out(sprintf("Couldn't save '%s' as blocked server", $domain));
155                                 return 1;
156                         }
157
158                 }
159         }
160
161         /**
162          * Prints the whole list of blocked domains including the reason
163          *
164          /* @param IConfig $config
165          */
166         private function printBlockedServers(IConfig $config)
167         {
168                 $table = new Console_Table();
169                 $table->setHeaders(['Domain', 'Reason']);
170                 $blocklist = $config->get('system', 'blocklist', []);
171                 foreach ($blocklist as $domain) {
172                         $table->addRow($domain);
173                 }
174                 $this->out($table->getTable());
175         }
176
177         /**
178          * Adds a server to the blocked list
179          *
180          * @param IConfig $config
181          *
182          * @return int The return code (0 = success, 1 = failed)
183          */
184         private function addBlockedServer(IConfig $config)
185         {
186                 if (count($this->args) < 2 || count($this->args) > 3) {
187                         throw new CommandArgsException('Add needs a domain and optional a reason.');
188                 }
189
190                 $domain = $this->getArgument(1);
191                 $reason = (count($this->args) === 3) ? $this->getArgument(2) : self::DEFAULT_REASON;
192
193                 $update = false;
194
195                 $currBlockList = $config->get('system', 'blocklist', []);
196                 $newBlockList = [];
197                 foreach ($currBlockList  as $blocked) {
198                         if ($blocked['domain'] === $domain) {
199                                 $update = true;
200                                 $newBlockList[] = [
201                                         'domain' => $domain,
202                                         'reason' => $reason,
203                                 ];
204                         } else {
205                                 $newBlockList[] = $blocked;
206                         }
207                 }
208
209                 if (!$update) {
210                         $newBlockList[] = [
211                                 'domain' => $domain,
212                                 'reason' => $reason,
213                         ];
214                 }
215
216                 if ($config->set('system', 'blocklist', $newBlockList)) {
217                         if ($update) {
218                                 $this->out(sprintf("The domain '%s' is now updated. (Reason: '%s')", $domain, $reason));
219                         } else {
220                                 $this->out(sprintf("The domain '%s' is now blocked. (Reason: '%s')", $domain, $reason));
221                         }
222                         return 0;
223                 } else {
224                         $this->out(sprintf("Couldn't save '%s' as blocked server", $domain));
225                         return 1;
226                 }
227         }
228
229         /**
230          * Removes a server from the blocked list
231          *
232          * @param IConfig $config
233          *
234          * @return int The return code (0 = success, 1 = failed)
235          */
236         private function removeBlockedServer(IConfig $config)
237         {
238                 if (count($this->args) !== 2) {
239                         throw new CommandArgsException('Remove needs a second parameter.');
240                 }
241
242                 $domain = $this->getArgument(1);
243
244                 $found = false;
245
246                 $currBlockList = $config->get('system', 'blocklist', []);
247                 $newBlockList = [];
248                 foreach ($currBlockList as $blocked) {
249                         if ($blocked['domain'] === $domain) {
250                                 $found = true;
251                         } else {
252                                 $newBlockList[] = $blocked;
253                         }
254                 }
255
256                 if (!$found) {
257                         $this->out(sprintf("The domain '%s' is not blocked.", $domain));
258                         return 1;
259                 }
260
261                 if ($config->set('system', 'blocklist', $newBlockList)) {
262                         $this->out(sprintf("The domain '%s' is not more blocked", $domain));
263                         return 0;
264                 } else {
265                         $this->out(sprintf("Couldn't remove '%s' from blocked servers", $domain));
266                         return 1;
267                 }
268         }
269 }