PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
ZipExtractor Class Reference

#include <ZipExtractor.h>

Classes

struct  Result
 

Static Public Member Functions

static Result ExtractAll (const std::string &zipPath, const std::string &destPath)
 
static Result ExtractFile (const std::string &zipPath, const std::string &fileInZip, const std::string &destPath)
 
static Result ReadFile (const std::string &zipPath, const std::string &fileInZip, std::string &outContents)
 
static Result ListContents (const std::string &zipPath, std::vector< std::string > &outFiles)
 
static bool ContainsFile (const std::string &zipPath, const std::string &fileInZip)
 
static Result Validate (const std::string &zipPath)
 
static Result ValidateSnapshot (const std::string &zipPath)
 

Detailed Description

ZipExtractor - Cross-platform ZIP file extraction utility

Uses libzip to extract PEBL snapshot packages and validate their contents. Supports reading files without full extraction for validation dialogs.

Definition at line 13 of file ZipExtractor.h.

Member Function Documentation

◆ ContainsFile()

bool ZipExtractor::ContainsFile ( const std::string &  zipPath,
const std::string &  fileInZip 
)
static

Check if ZIP contains a specific file

Parameters
zipPathPath to ZIP file
fileInZipPath of file to check for
Returns
true if file exists in ZIP, false otherwise

Definition at line 247 of file ZipExtractor.cpp.

248 {
249 int error = 0;
250 zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &error);
251
252 if (!archive) {
253 return false;
254 }
255
256 zip_int64_t index = zip_name_locate(archive, fileInZip.c_str(), 0);
257 zip_close(archive);
258
259 return index >= 0;
260}

Referenced by ValidateSnapshot().

◆ ExtractAll()

ZipExtractor::Result ZipExtractor::ExtractAll ( const std::string &  zipPath,
const std::string &  destPath 
)
static

Extract entire ZIP archive to destination directory Creates destination directory if it doesn't exist

Parameters
zipPathPath to ZIP file
destPathDestination directory (will be created)
Returns
Result with success status and error message if failed

Definition at line 18 of file ZipExtractor.cpp.

19 {
20 int error = 0;
21 zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &error);
22
23 if (!archive) {
24 zip_error_t ziperror;
25 zip_error_init_with_code(&ziperror, error);
26 std::string msg = "Failed to open ZIP: " + std::string(zip_error_strerror(&ziperror));
27 zip_error_fini(&ziperror);
28 return Result(false, msg);
29 }
30
31 // Get number of files in archive
32 zip_int64_t numEntries = zip_get_num_entries(archive, 0);
33 if (numEntries < 0) {
34 zip_close(archive);
35 return Result(false, "Failed to get entry count");
36 }
37
38 // Create destination directory if it doesn't exist
39 if (!CreateDirectories(destPath)) {
40 zip_close(archive);
41 return Result(false, "Failed to create destination directory: " + destPath);
42 }
43
44 // Extract each file
45 for (zip_int64_t i = 0; i < numEntries; i++) {
46 // Get file stats
47 struct zip_stat st;
48 zip_stat_init(&st);
49 if (zip_stat_index(archive, i, 0, &st) != 0) {
50 zip_close(archive);
51 return Result(false, "Failed to stat file at index " + std::to_string(i));
52 }
53
54 std::string filename = st.name;
55
56 // Skip directories (they end with /)
57 if (filename.back() == '/') {
58 // Create directory
59 std::string dirPath = destPath + PATH_SEPARATOR + filename;
60 CreateDirectories(dirPath);
61 continue;
62 }
63
64 // Build destination path
65 std::string destFile = destPath + PATH_SEPARATOR + filename;
66
67 // Create parent directories if needed
68 size_t lastSlash = destFile.find_last_of("/\\");
69 if (lastSlash != std::string::npos) {
70 std::string dirPath = destFile.substr(0, lastSlash);
71 if (!CreateDirectories(dirPath)) {
72 zip_close(archive);
73 return Result(false, "Failed to create directory: " + dirPath);
74 }
75 }
76
77 // Open file in ZIP
78 zip_file_t* file = zip_fopen_index(archive, i, 0);
79 if (!file) {
80 zip_close(archive);
81 return Result(false, "Failed to open file in ZIP: " + filename);
82 }
83
84 // Read file contents
85 std::vector<char> buffer(st.size);
86 zip_int64_t bytesRead = zip_fread(file, buffer.data(), st.size);
87 zip_fclose(file);
88
89 if (bytesRead != static_cast<zip_int64_t>(st.size)) {
90 zip_close(archive);
91 return Result(false, "Failed to read file: " + filename);
92 }
93
94 // Write to destination
95 std::ofstream out(destFile, std::ios::binary);
96 if (!out) {
97 zip_close(archive);
98 return Result(false, "Failed to create file: " + destFile);
99 }
100 out.write(buffer.data(), bytesRead);
101 out.close();
102 }
103
104 zip_close(archive);
105 return Result(true);
106}
#define PATH_SEPARATOR

References PATH_SEPARATOR.

◆ ExtractFile()

ZipExtractor::Result ZipExtractor::ExtractFile ( const std::string &  zipPath,
const std::string &  fileInZip,
const std::string &  destPath 
)
static

Extract single file from ZIP to destination path

Parameters
zipPathPath to ZIP file
fileInZipPath of file within ZIP (e.g., "study-info.json")
destPathDestination file path
Returns
Result with success status and error message if failed

Definition at line 109 of file ZipExtractor.cpp.

111 {
112 int error = 0;
113 zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &error);
114
115 if (!archive) {
116 return Result(false, "Failed to open ZIP");
117 }
118
119 // Find file by name
120 zip_int64_t index = zip_name_locate(archive, fileInZip.c_str(), 0);
121 if (index < 0) {
122 zip_close(archive);
123 return Result(false, "File not found in ZIP: " + fileInZip);
124 }
125
126 // Get file stats
127 struct zip_stat st;
128 zip_stat_init(&st);
129 if (zip_stat_index(archive, index, 0, &st) != 0) {
130 zip_close(archive);
131 return Result(false, "Failed to stat file");
132 }
133
134 // Open and read file
135 zip_file_t* file = zip_fopen_index(archive, index, 0);
136 if (!file) {
137 zip_close(archive);
138 return Result(false, "Failed to open file");
139 }
140
141 std::vector<char> buffer(st.size);
142 zip_int64_t bytesRead = zip_fread(file, buffer.data(), st.size);
143 zip_fclose(file);
144 zip_close(archive);
145
146 if (bytesRead != static_cast<zip_int64_t>(st.size)) {
147 return Result(false, "Failed to read file");
148 }
149
150 // Create parent directories for destination
151 size_t lastSlash = destPath.find_last_of("/\\");
152 if (lastSlash != std::string::npos) {
153 std::string dirPath = destPath.substr(0, lastSlash);
154 if (!CreateDirectories(dirPath)) {
155 return Result(false, "Failed to create directory: " + dirPath);
156 }
157 }
158
159 // Write to destination
160 std::ofstream out(destPath, std::ios::binary);
161 if (!out) {
162 return Result(false, "Failed to create file: " + destPath);
163 }
164 out.write(buffer.data(), bytesRead);
165 out.close();
166
167 return Result(true);
168}

◆ ListContents()

ZipExtractor::Result ZipExtractor::ListContents ( const std::string &  zipPath,
std::vector< std::string > &  outFiles 
)
static

List all files and directories in ZIP

Parameters
zipPathPath to ZIP file
outFilesVector to receive list of file paths
Returns
Result with success status and error message if failed

Definition at line 219 of file ZipExtractor.cpp.

220 {
221 int error = 0;
222 zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &error);
223
224 if (!archive) {
225 return Result(false, "Failed to open ZIP");
226 }
227
228 zip_int64_t numEntries = zip_get_num_entries(archive, 0);
229 if (numEntries < 0) {
230 zip_close(archive);
231 return Result(false, "Failed to get entry count");
232 }
233
234 outFiles.clear();
235 for (zip_int64_t i = 0; i < numEntries; i++) {
236 const char* name = zip_get_name(archive, i, 0);
237 if (name) {
238 outFiles.push_back(name);
239 }
240 }
241
242 zip_close(archive);
243 return Result(true);
244}

Referenced by ValidateSnapshot().

◆ ReadFile()

ZipExtractor::Result ZipExtractor::ReadFile ( const std::string &  zipPath,
const std::string &  fileInZip,
std::string &  outContents 
)
static

Read file contents from ZIP without extracting Useful for validation and preview before extraction

Parameters
zipPathPath to ZIP file
fileInZipPath of file within ZIP
outContentsString to receive file contents
Returns
Result with success status and error message if failed

Definition at line 171 of file ZipExtractor.cpp.

173 {
174 int error = 0;
175 zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &error);
176
177 if (!archive) {
178 return Result(false, "Failed to open ZIP");
179 }
180
181 // Find file by name
182 zip_int64_t index = zip_name_locate(archive, fileInZip.c_str(), 0);
183 if (index < 0) {
184 zip_close(archive);
185 return Result(false, "File not found in ZIP: " + fileInZip);
186 }
187
188 // Get file stats
189 struct zip_stat st;
190 zip_stat_init(&st);
191 if (zip_stat_index(archive, index, 0, &st) != 0) {
192 zip_close(archive);
193 return Result(false, "Failed to stat file");
194 }
195
196 // Open and read file
197 zip_file_t* file = zip_fopen_index(archive, index, 0);
198 if (!file) {
199 zip_close(archive);
200 return Result(false, "Failed to open file");
201 }
202
203 std::vector<char> buffer(st.size + 1); // +1 for null terminator
204 zip_int64_t bytesRead = zip_fread(file, buffer.data(), st.size);
205 zip_fclose(file);
206 zip_close(archive);
207
208 if (bytesRead != static_cast<zip_int64_t>(st.size)) {
209 return Result(false, "Failed to read file");
210 }
211
212 buffer[st.size] = '\0';
213 outContents = std::string(buffer.data());
214
215 return Result(true);
216}

◆ Validate()

ZipExtractor::Result ZipExtractor::Validate ( const std::string &  zipPath)
static

Validate ZIP file integrity Checks if file is a valid ZIP and not corrupted

Parameters
zipPathPath to ZIP file
Returns
Result with success status and error message if invalid

Definition at line 263 of file ZipExtractor.cpp.

263 {
264 int error = 0;
265 zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY | ZIP_CHECKCONS, &error);
266
267 if (!archive) {
268 zip_error_t ziperror;
269 zip_error_init_with_code(&ziperror, error);
270 std::string msg = std::string(zip_error_strerror(&ziperror));
271 zip_error_fini(&ziperror);
272 return Result(false, "Invalid or corrupted ZIP: " + msg);
273 }
274
275 zip_close(archive);
276 return Result(true);
277}

Referenced by ValidateSnapshot().

◆ ValidateSnapshot()

ZipExtractor::Result ZipExtractor::ValidateSnapshot ( const std::string &  zipPath)
static

Validate that ZIP is a PEBL snapshot package Checks for required files: study-info.json, tests/ directory

Parameters
zipPathPath to ZIP file
Returns
Result with success status and error message if not valid snapshot

Definition at line 280 of file ZipExtractor.cpp.

280 {
281 // First validate as ZIP
282 Result validZip = Validate(zipPath);
283 if (!validZip.success) {
284 return validZip;
285 }
286
287 // Check for required files
288 if (!ContainsFile(zipPath, "study-info.json")) {
289 return Result(false, "Not a PEBL snapshot: missing study-info.json");
290 }
291
292 // Check for tests directory (at least one file starting with "tests/")
293 std::vector<std::string> files;
294 Result listResult = ListContents(zipPath, files);
295 if (!listResult.success) {
296 return listResult;
297 }
298
299 bool hasTests = false;
300 for (const auto& file : files) {
301 if (file.find("tests/") == 0) {
302 hasTests = true;
303 break;
304 }
305 }
306
307 if (!hasTests) {
308 return Result(false, "Not a PEBL snapshot: missing tests/ directory");
309 }
310
311 return Result(true);
312}
static bool ContainsFile(const std::string &zipPath, const std::string &fileInZip)
static Result ListContents(const std::string &zipPath, std::vector< std::string > &outFiles)
static Result Validate(const std::string &zipPath)

References ContainsFile(), ListContents(), ZipExtractor::Result::success, and Validate().


The documentation for this class was generated from the following files: