]> BookStack Code Mirror - bookstack/blob - app/Exports/ZipExports/ZipExportReader.php
6b88ef61c33f45fbe966bae68c3d95dde274e1af
[bookstack] / app / Exports / ZipExports / ZipExportReader.php
1 <?php
2
3 namespace BookStack\Exports\ZipExports;
4
5 use BookStack\Exceptions\ZipExportException;
6 use BookStack\Exports\ZipExports\Models\ZipExportBook;
7 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
8 use BookStack\Exports\ZipExports\Models\ZipExportModel;
9 use BookStack\Exports\ZipExports\Models\ZipExportPage;
10 use BookStack\Util\WebSafeMimeSniffer;
11 use ZipArchive;
12
13 class ZipExportReader
14 {
15     protected ZipArchive $zip;
16     protected bool $open = false;
17
18     public function __construct(
19         protected string $zipPath,
20     ) {
21         $this->zip = new ZipArchive();
22     }
23
24     /**
25      * @throws ZipExportException
26      */
27     protected function open(): void
28     {
29         if ($this->open) {
30             return;
31         }
32
33         // Validate file exists
34         if (!file_exists($this->zipPath) || !is_readable($this->zipPath)) {
35             throw new ZipExportException(trans('errors.import_zip_cant_read'));
36         }
37
38         // Validate file is valid zip
39         $opened = $this->zip->open($this->zipPath, ZipArchive::RDONLY);
40         if ($opened !== true) {
41             throw new ZipExportException(trans('errors.import_zip_cant_read'));
42         }
43
44         $this->open = true;
45     }
46
47     public function close(): void
48     {
49         if ($this->open) {
50             $this->zip->close();
51             $this->open = false;
52         }
53     }
54
55     /**
56      * @throws ZipExportException
57      */
58     public function readData(): array
59     {
60         $this->open();
61
62         // Validate json data exists, including metadata
63         $jsonData = $this->zip->getFromName('data.json') ?: '';
64         $importData = json_decode($jsonData, true);
65         if (!$importData) {
66             throw new ZipExportException(trans('errors.import_zip_cant_decode_data'));
67         }
68
69         return $importData;
70     }
71
72     public function fileExists(string $fileName): bool
73     {
74         return $this->zip->statName("files/{$fileName}") !== false;
75     }
76
77     /**
78      * @return false|resource
79      */
80     public function streamFile(string $fileName)
81     {
82         return $this->zip->getStream("files/{$fileName}");
83     }
84
85     /**
86      * Sniff the mime type from the file of given name.
87      */
88     public function sniffFileMime(string $fileName): string
89     {
90         $stream = $this->streamFile($fileName);
91         $sniffContent = fread($stream, 2000);
92
93         return (new WebSafeMimeSniffer())->sniff($sniffContent);
94     }
95
96     /**
97      * @throws ZipExportException
98      */
99     public function decodeDataToExportModel(): ZipExportBook|ZipExportChapter|ZipExportPage
100     {
101         $data = $this->readData();
102         if (isset($data['book'])) {
103             return ZipExportBook::fromArray($data['book']);
104         } else if (isset($data['chapter'])) {
105             return ZipExportChapter::fromArray($data['chapter']);
106         } else if (isset($data['page'])) {
107             return ZipExportPage::fromArray($data['page']);
108         }
109
110         throw new ZipExportException("Could not identify content in ZIP file data.");
111     }
112 }