3 * @copyright Copyright (C) 2010-2022, the Friendica project
5 * @license GNU AGPL version 3 or any later version
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.
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.
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/>.
22 namespace Friendica\Console;
24 use Asika\SimpleConsole\CommandArgsException;
25 use Asika\SimpleConsole\Console;
27 use Friendica\Core\Config\Capability\IManageConfigValues;
30 * Manage blocked servers
32 * With this tool, you can list the current blocked servers
33 * or you can add / remove a blocked server from the list
35 class ServerBlock extends Console
37 const DEFAULT_REASON = 'blocked';
39 protected $helpOptions = ['h', 'help', '?'];
42 * @var IManageConfigValues
46 protected function getHelp()
49 console serverblock - Manage blocked server domain patterns
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>
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.
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...
69 -h|--help|-? Show help information
70 -v Show more debug information.
75 public function __construct(IManageConfigValues $config, $argv = null)
77 parent::__construct($argv);
79 $this->config = $config;
82 protected function doExecute(): int
84 if (count($this->args) == 0) {
85 $this->printBlockedServers($this->config);
89 switch ($this->getArgument(0)) {
91 return $this->addBlockedServer($this->config);
93 return $this->removeBlockedServer($this->config);
95 return $this->exportBlockedServers($this->config);
97 return $this->importBlockedServers($this->config);
99 throw new CommandArgsException('Unknown command.');
105 * Exports the list of blocked domains including the reason for the
106 * block to a CSV file.
108 * @param IManageConfigValues $config
110 private function exportBlockedServers(IManageConfigValues $config)
112 $filename = $this->getArgument(1);
113 $blocklist = $config->get('system', 'blocklist', []);
114 $fp = fopen($filename, 'w');
116 throw new Exception(sprintf('The file "%s" could not be created.', $filename));
118 foreach ($blocklist as $domain) {
119 fputcsv($fp, $domain);
126 * Imports a list of domains and a reason for the block from a CSV
127 * file, e.g. created with the export function.
129 * @param IManageConfigValues $config
131 private function importBlockedServers(IManageConfigValues $config)
133 $filename = $this->getArgument(1);
134 $currBlockList = $config->get('system', 'blocklist', []);
137 if (($fp = fopen($filename, 'r')) !== false) {
138 while (($data = fgetcsv($fp, 1000, ',')) !== false) {
140 if (count($data) == 0) {
141 $reason = self::DEFAULT_REASON;
149 if (!in_array($data, $newBlockList)) {
150 $newBlockList[] = $data;
154 foreach ($currBlockList as $blocked) {
155 if (!in_array($blocked, $newBlockList)) {
156 $newBlockList[] = $blocked;
160 if ($config->set('system', 'blocklist', $newBlockList)) {
161 $this->out(sprintf("Entries from %s that were not blocked before are now blocked", $filename));
164 $this->out(sprintf("Couldn't save '%s' as blocked server", $domain));
168 throw new Exception(sprintf('The file "%s" could not be opened for importing', $filename));
173 * Prints the whole list of blocked domains including the reason
175 * @param IManageConfigValues $config
177 private function printBlockedServers(IManageConfigValues $config)
179 $table = new Console_Table();
180 $table->setHeaders(['Domain', 'Reason']);
181 $blocklist = $config->get('system', 'blocklist', []);
182 foreach ($blocklist as $domain) {
183 $table->addRow($domain);
185 $this->out($table->getTable());
192 * Adds a server to the blocked list
194 * @param IManageConfigValues $config
196 * @return int The return code (0 = success, 1 = failed)
198 private function addBlockedServer(IManageConfigValues $config)
200 if (count($this->args) < 2 || count($this->args) > 3) {
201 throw new CommandArgsException('Add needs a domain and optional a reason.');
204 $domain = $this->getArgument(1);
205 $reason = (count($this->args) === 3) ? $this->getArgument(2) : self::DEFAULT_REASON;
209 $currBlockList = $config->get('system', 'blocklist', []);
211 foreach ($currBlockList as $blocked) {
212 if ($blocked['domain'] === $domain) {
219 $newBlockList[] = $blocked;
230 if ($config->set('system', 'blocklist', $newBlockList)) {
232 $this->out(sprintf("The domain '%s' is now updated. (Reason: '%s')", $domain, $reason));
234 $this->out(sprintf("The domain '%s' is now blocked. (Reason: '%s')", $domain, $reason));
238 $this->out(sprintf("Couldn't save '%s' as blocked server", $domain));
244 * Removes a server from the blocked list
246 * @param IManageConfigValues $config
248 * @return int The return code (0 = success, 1 = failed)
250 private function removeBlockedServer(IManageConfigValues $config)
252 if (count($this->args) !== 2) {
253 throw new CommandArgsException('Remove needs a second parameter.');
256 $domain = $this->getArgument(1);
260 $currBlockList = $config->get('system', 'blocklist', []);
262 foreach ($currBlockList as $blocked) {
263 if ($blocked['domain'] === $domain) {
266 $newBlockList[] = $blocked;
271 $this->out(sprintf("The domain '%s' is not blocked.", $domain));
275 if ($config->set('system', 'blocklist', $newBlockList)) {
276 $this->out(sprintf("The domain '%s' is not more blocked", $domain));
279 $this->out(sprintf("Couldn't remove '%s' from blocked servers", $domain));