Browse Source

CLI for OPML import, and proper exceptions

microsub
J. King 5 years ago
parent
commit
b9821d925a
  1. 6
      lib/AbstractException.php
  2. 11
      lib/CLI.php
  3. 19
      lib/ImportExport/OPML.php
  4. 18
      locale/en.php

6
lib/AbstractException.php

@ -86,8 +86,14 @@ abstract class AbstractException extends \Exception {
"Feed/Exception.xmlEntity" => 10512,
"Feed/Exception.subscriptionNotFound" => 10521,
"Feed/Exception.unsupportedFeedFormat" => 10522,
"ImportExport/Exception.fileMissing" => 10601,
"ImportExport/Exception.fileUnreadable" => 10603,
"ImportExport/Exception.fileUnwritable" => 10604,
"ImportExport/Exception.fileUncreatable" => 10605,
"ImportExport/Exception.invalidSyntax" => 10611,
"ImportExport/Exception.invalidSemantics" => 10612,
"ImportExport/Exception.invalidFolderName" => 10613,
"ImportExport/Exception.invalidFolderCopy" => 10614,
];
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {

11
lib/CLI.php

@ -24,7 +24,10 @@ Usage:
arsse.php user unset-pass <username>
[--oldpass=<pass>] [--fever]
arsse.php user auth <username> <password> [--fever]
arsse.php export <username> [<file>] [-f | --flat]
arsse.php export <username> [<file>]
[-f | --flat]
arsse.php import <username> [<file>]
[-f | --flat] [-r | --replace]
arsse.php --version
arsse.php --help | -h
@ -70,7 +73,7 @@ USAGE_TEXT;
'help' => false,
]);
try {
$cmd = $this->command(["--help", "--version", "daemon", "feed refresh", "feed refresh-all", "conf save-defaults", "user", "export"], $args);
$cmd = $this->command(["--help", "--version", "daemon", "feed refresh", "feed refresh-all", "conf save-defaults", "user", "export", "import"], $args);
if ($cmd && !in_array($cmd, ["--help", "--version", "conf save-defaults"])) {
// only certain commands don't require configuration to be loaded
$this->loadConf();
@ -99,6 +102,10 @@ USAGE_TEXT;
$u = $args['<username>'];
$file = $this->resolveFile($args['<file>'], "w");
return (int) !$this->getInstance(OPML::class)->exportFile($file, $u, $args['--flat']);
case "import":
$u = $args['<username>'];
$file = $this->resolveFile($args['<file>'], "w");
return (int) !$this->getInstance(OPML::class)->importFile($file, $u, $args['--flat'], $args['--replace']);
}
} catch (AbstractException $e) {
$this->logError($e->getMessage());

19
lib/ImportExport/OPML.php

@ -19,14 +19,14 @@ class OPML {
foreach ($folders as $f) {
// check to make sure folder names are all valid
if (!strlen(trim($f['name']))) {
throw new \Exception;
throw new Exception("invalidFolderName");
}
// check for duplicates
if (!isset($folderMap[$f['parent']])) {
$folderMap[$f['parent']] = [];
}
if (isset($folderMap[$f['parent']][$f['name']])) {
throw new \Exception;
throw new Exception("invalidFolderCopy");
} else {
$folderMap[$f['parent']][$f['name']] = true;
}
@ -142,12 +142,13 @@ class OPML {
$d = new \DOMDocument;
if (!@$d->loadXML($opml)) {
// not a valid XML document
throw new \Exception;
$err = libxml_get_last_error();
throw new Exception("invalidSyntax", ['line' => $err->line, 'column' => $err->column]);
}
$body = $d->getElementsByTagName("body");
if ($d->documentElement->nodeName !== "opml" || !$body->length || $body->item(0)->parentNode != $d->documentElement) {
// not a valid OPML document
throw new \Exception;
throw new Exception("invalidSemantics", ['type' => "OPML"]);
}
$body = $body->item(0);
$folders = [];
@ -268,4 +269,14 @@ class OPML {
}
return true;
}
public function imortFile(string $file, string $user, bool $flat = false, bool $replace): bool {
$data = @file_get_contents($file);
if ($data === false) {
// if it fails throw an exception
$err = file_exists($file) ? "fileUnreadable" : "fileMissing";
throw new Exception($err, ['file' => $file, 'format' => str_replace(__NAMESPACE__."\\", "", __CLASS__)]);
}
return $this->import($user, $data, $flat, $replace);
}
}

18
locale/en.php

@ -155,14 +155,12 @@ return [
'Exception.JKingWeb/Arsse/Feed/Exception.xmlEntity' => 'Refused to parse feed "{url}" because it contains an XXE attack',
'Exception.JKingWeb/Arsse/Feed/Exception.subscriptionNotFound' => 'Unable to find a feed at location "{url}"',
'Exception.JKingWeb/Arsse/Feed/Exception.unsupportedFeedFormat' => 'Feed "{url}" is of an unsupported format',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUncreatable' =>
'Insufficient permissions to write {type, select,
OPML {OPML}
other {"{type}"}
} export to file "{file}"',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUnwritable' =>
'Insufficient permissions to write {type, select,
OPML {OPML}
other {"{type}"}
} export to existing file "{file}"',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileMissing' => 'Import {type} file "{file}" does not exist',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUnreadable' => 'Insufficient permissions to read {type} file "{file}" for import',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUncreatable' => 'Insufficient permissions to write {type} export to file "{file}"',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUnwritable' => 'Insufficient permissions to write {type} export to existing file "{file}"',
'Exception.JKingWeb/Arsse/ImportExport/Exception.invalidSyntax' => 'Input data syntax error at line {line}, column {column}',
'Exception.JKingWeb/Arsse/ImportExport/Exception.invalidSemantics' => 'Input data is not valid {type} data',
'Exception.JKingWeb/Arsse/ImportExport/Exception.invalidFolderName' => 'Input data contains an invalid folder name',
'Exception.JKingWeb/Arsse/ImportExport/Exception.invalidFolderCopy' => 'Input data contains multiple folders of the same name under the same parent',
];

Loading…
Cancel
Save