arsse.php conf save-defaults [] arsse.php user [list] arsse.php user add [] [--admin] arsse.php user remove arsse.php user show arsse.php user set arsse.php user unset arsse.php user set-pass [] [--oldpass=] [--fever] arsse.php user unset-pass [--oldpass=] [--fever] arsse.php user auth [--fever] arsse.php import [] [-f | --flat] [-r | --replace] arsse.php export [] [-f | --flat] arsse.php --version arsse.php -h | --help The Arsse command-line interface can be used to perform various administrative tasks such as starting the newsfeed refresh service, managing users, and importing or exporting data. Commands: daemon Starts the newsfeed refreshing service, which will refresh stale feeds at the configured interval automatically. feed refresh-all Refreshes any stale feeds once, then exits. This performs the same function as the daemon command without looping; this is useful if use of a scheduler such a cron is preferred over a persitent service. feed refresh Refreshes a single feed by numeric ID. This is principally for internal use as the feed ID numbers are not usually exposed to the user. conf save-defaults [] Prints default configuration parameters to standard output, or to if specified. Each parameter is annotated with a short description of its purpose and usage. user [list] Prints a list of all existing users, one per line. user add [] [--admin] Adds the user specified by , with the provided password . If no password is specified, a random password will be generated and printed to standard output. The --admin option will make the user an administrator, which allows them to manage users via the Miniflux protocol, among other things. user remove Removes the user specified by . Data related to the user, including folders and subscriptions, are immediately deleted. Feeds to which the user was subscribed will be retained and refreshed until the configured retention time elapses. user show Displays the metadata of a user in a basic tabular format. See below for details on the various properties displayed. user set Sets a user's metadata proprty to the supplied value. See below for details on the various properties available. user unset Sets a user's metadata proprty to its default value. See below for details on the various properties available. What the default value for a property evaluates to depends on which protocol is used. user set-pass [] Changes 's password to . If no password is specified, a random password will be generated and printed to standard output. The --oldpass= option can be used to supply a user's exiting password if this is required by the authentication driver to change a password. Currently this is not used by any existing driver. The --fever option sets a user's Fever protocol password instead of their general password. As Fever requires that passwords be stored insecurely, users do not have Fever passwords by default, and logging in to the Fever protocol is disabled until a password is set. It is highly recommended that a user's Fever password be different from their general password. user unset-pass Unsets a user's password, effectively disabling their account. As with password setting, the --oldpass and --fever options may be used. user auth Tests logging in as with password . This only checks that the user's password is correctly recognized; it has no side effects. The --fever option may be used to test the user's Fever protocol password, if any. import [] Imports the feeds, folders, and tags found in the OPML formatted into the account of . If no file is specified, data is instead read from standard input. The --replace option interprets the OPML file as the list of all desired feeds, folders and tags, performing any deletion or moving of existing entries which do not appear in the flle. If this option is not specified, the file is assumed to list desired additions only. The --flat option can be used to ignore any folder structures in the file, importing any feeds only into the root folder. export [] Exports 's feeds, folders, and tags to the OPML file specified by , or standard output if none is provided. Note that due to a limitation of the OPML format, any commas present in tag names will not be retained in the export. The --flat option can be used to omit folders from the export. Some OPML implementations may not support folders, or arbitrary nesting; this option may be used when planning to import into such software. User metadata: User metadata are primarily used by the Miniflux protocol, and most properties have identical or similar names to those used by Miniflux. Properties may also affect other protocols, or conversely may have no effect even when using the Miniflux protocol; this is noted below when appropriate. Booleans accept any of the values true/false, 1/0, yes/no, on/off. The following metadata properties exist for each user: num Integer. The numeric identifier of the user. This is assigned at user creation and is read-only. admin Boolean. Whether the user is an administrator. Administrators may manage other users via the Miniflux protocol, and also may trigger feed updates manually via the Nextcloud News protocol. lang String. The preferred language of the user, as a BCP 47 language tag e.g. "en-ca". Note that since The Arsse currently only includes English text it is not used by The Arsse itself, but clients may use this metadatum in protocols which expose it. tz String. The time zone of the user, as a tzdata identifier e.g. "America/Los_Angeles". root_folder_name String. The name of the root folder, in protocols which allow it to be renamed. sort_asc Boolean. Whether the user prefers ascending sort order for articles. Descending order is usually the default, but explicitly setting this property false will also make a preference for descending order explicit. theme String. The user's preferred theme. This is not used by The Arsse itself, but clients may use this metadatum in protocols which expose it. page_size Integer. The user's preferred page size when listing articles. This is not used by The Arsse itself, but clients may use this metadatum in protocols which expose it. shortcuts Boolean. Whether to enable keyboard shortcuts. This is not used by The Arsse itself, but clients may use this metadatum in protocols which expose it. gestures Boolean. Whether to enable touch gestures. This is not used by The Arsse itself, but clients may use this metadatum in protocols which expose it. reading_time Boolean. Whether to calculate and display the estimated reading time for articles. Currently The Arsse does not calculate reading time, so changing this will likely have no effect. stylesheet String. A user CSS stylesheet. This is not used by The Arsse itself, but clients may use this metadatum in protocols which expose it. USAGE_TEXT; protected function usage($prog): string { $prog = basename($prog); return str_replace("arsse.php", $prog, self::USAGE); } protected function command(array $options, $args): string { foreach ($options as $cmd) { foreach (explode(" ", $cmd) as $part) { if (!$args[$part]) { continue 2; } } return $cmd; } return ""; } /** @codeCoverageIgnore */ protected function loadConf(): bool { $conf = file_exists(BASE."config.php") ? new Conf(BASE."config.php") : new Conf; Arsse::load($conf); return true; } protected function resolveFile($file, string $mode): string { // TODO: checking read/write permissions on the provided path may be useful $stdinOrStdout = in_array($mode, ["r", "r+"]) ? "php://input" : "php://output"; return ($file === "-" ? null : $file) ?? $stdinOrStdout; } public function dispatch(array $argv = null): int { $argv = $argv ?? $_SERVER['argv']; $argv0 = array_shift($argv); $args = \Docopt::handle($this->usage($argv0), [ 'argv' => $argv, 'help' => false, ]); try { $cmd = $this->command(["-h", "--help", "--version", "daemon", "feed refresh", "feed refresh-all", "conf save-defaults", "user", "export", "import"], $args); if ($cmd && !in_array($cmd, ["-h", "--help", "--version", "conf save-defaults"])) { // only certain commands don't require configuration to be loaded $this->loadConf(); } switch ($cmd) { case "-h": case "--help": echo $this->usage($argv0).\PHP_EOL; return 0; case "--version": echo Arsse::VERSION.\PHP_EOL; return 0; case "daemon": Arsse::$obj->get(Service::class)->watch(true); return 0; case "feed refresh": return (int) !Arsse::$db->feedUpdate((int) $args[''], true); case "feed refresh-all": Arsse::$obj->get(Service::class)->watch(false); return 0; case "conf save-defaults": $file = $this->resolveFile($args[''], "w"); return (int) !Arsse::$obj->get(Conf::class)->exportFile($file, true); case "user": return $this->userManage($args); case "export": $u = $args['']; $file = $this->resolveFile($args[''], "w"); return (int) !Arsse::$obj->get(OPML::class)->exportFile($file, $u, ($args['--flat'] || $args['-f'])); case "import": $u = $args['']; $file = $this->resolveFile($args[''], "r"); return (int) !Arsse::$obj->get(OPML::class)->importFile($file, $u, ($args['--flat'] || $args['-f']), ($args['--replace'] || $args['-r'])); } } catch (AbstractException $e) { $this->logError($e->getMessage()); return $e->getCode(); } } // @codeCoverageIgnore /** @codeCoverageIgnore */ protected function logError(string $msg): void { fwrite(STDERR, $msg.\PHP_EOL); } protected function userManage($args): int { $cmd = $this->command(["add", "remove", "show", "set", "unset", "set-pass", "unset-pass", "list", "auth"], $args); switch ($cmd) { case "add": $out = $this->userAddOrSetPassword("add", $args[""], $args[""]); if ($args['--admin']) { Arsse::$user->propertiesSet($args[""], ['admin' => true]); } return $out; case "set-pass": if ($args['--fever']) { $passwd = Arsse::$obj->get(Fever::class)->register($args[""], $args[""]); if (is_null($args[""])) { echo $passwd.\PHP_EOL; } return 0; } else { return $this->userAddOrSetPassword("passwordSet", $args[""], $args[""], $args["--oldpass"]); } // no break case "unset-pass": if ($args['--fever']) { Arsse::$obj->get(Fever::class)->unregister($args[""]); } else { Arsse::$user->passwordUnset($args[""], $args["--oldpass"]); } return 0; case "remove": return (int) !Arsse::$user->remove($args[""]); case "show": return $this->userShowProperties($args[""]); case "set": return (int) !Arsse::$user->propertiesSet($args[""], [$args[""] => $args[""]]); case "unset": return (int) !Arsse::$user->propertiesSet($args[""], [$args[""] => null]); case "auth": return $this->userAuthenticate($args[""], $args[""], $args["--fever"]); case "list": case "": return $this->userList(); default: throw new Exception("constantUnknown", $cmd); // @codeCoverageIgnore } } protected function userAddOrSetPassword(string $method, string $user, string $password = null, string $oldpass = null): int { $passwd = Arsse::$user->$method(...array_slice(func_get_args(), 1)); if (is_null($password)) { echo $passwd.\PHP_EOL; } return 0; } protected function userList(): int { $list = Arsse::$user->list(); if ($list) { echo implode(\PHP_EOL, $list).\PHP_EOL; } return 0; } protected function userAuthenticate(string $user, string $password, bool $fever = false): int { $result = $fever ? Arsse::$obj->get(Fever::class)->authenticate($user, $password) : Arsse::$user->auth($user, $password); if ($result) { echo Arsse::$lang->msg("CLI.Auth.Success").\PHP_EOL; return 0; } else { echo Arsse::$lang->msg("CLI.Auth.Failure").\PHP_EOL; return 1; } } protected function userShowProperties(string $user): int { $data = Arsse::$user->propertiesGet($user); $len = array_reduce(array_keys($data), function($carry, $item) { return max($carry, strlen($item)); }, 0) + 2; foreach ($data as $k => $v) { echo str_pad($k, $len, " "); echo var_export($v, true).\PHP_EOL; } return 0; } }