1 /**
2 * @file zip.c zip module
3 *
4 * $Id: zip.c,v 1.14 2003/01/02 05:07:13 defrhymes Exp $
5 *
6 * @Copyright (C) 2001-2003 The GNUpdate Project.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23 #include <libcomprex/comprex.h>
24 #include <libcomprex/internal.h>
25 #include <zip.h>
26
27 static size_t
__readFuncZip(void * ptr,size_t size,size_t nmemb,CxFP * fp)28 __readFuncZip(void *ptr, size_t size, size_t nmemb, CxFP *fp)
29 {
30 ZipFile *fileData;
31 CxFile *file;
32 CxFP *parentFp;
33 size_t totalSize, remainingSize, result;
34
35 /* Get the data for the file within the archive */
36 file = fp->file;
37 fileData = (ZipFile *)fp->moduleData;
38
39 parentFp = (CxFP *)cxGetFileArchive(file)->moduleData;
40
41 /* you seek where I tell you to! */
42 if(cxTell(parentFp) != fileData->curOffset)
43 cxSeek(parentFp, fileData->curOffset, SEEK_SET);
44
45 totalSize = (size * nmemb);
46 remainingSize = cxGetFileCompressedSize(file) -
47 (fileData->curOffset - fileData->startOffset);
48
49 /* Don't read past the end of the file */
50 if(totalSize > remainingSize)
51 totalSize = remainingSize;
52
53 /* Read from the parent's fp, duh! */
54 result = cxRead(ptr, size, nmemb, parentFp);
55
56 return result;
57 }
58
59 static void
__seekFuncZip(CxFP * fp,long offset,int whence)60 __seekFuncZip (CxFP *fp, long offset, int whence)
61 {
62 CxFile *file;
63 ZipFile *fileData;
64
65 file = fp->file;
66 fileData = (ZipFile *)fp->moduleData;
67
68 switch(whence)
69 {
70 case SEEK_SET:
71 fileData->curOffset = fileData->startOffset + offset;
72 break;
73 case SEEK_CUR:
74 fileData->curOffset += offset;
75 break;
76 case SEEK_END:
77 fileData->curOffset = fileData->startOffset
78 + cxGetFileCompressedSize(file)
79 - offset;
80 break;
81 }
82 }
83
84 static size_t
__altReadFunc(void * ptr,size_t size,size_t nmemb,CxFP * fp)85 __altReadFunc (void *ptr, size_t size, size_t nmemb, CxFP *fp)
86 {
87 return 0;
88 }
89
90 static size_t
__writeFuncZip(const void * ptr,size_t size,size_t nmemb,CxFP * fp)91 __writeFuncZip (const void *ptr, size_t size, size_t nmemb, CxFP *fp)
92 {
93 return 0;
94 }
95
96 static void
__closeFuncZip(CxFP * fp)97 __closeFuncZip (CxFP *fp)
98 {
99 if (fp->moduleData != NULL)
100 {
101 free(fp->moduleData);
102 fp->moduleData = NULL;
103 }
104 }
105
106 static CxStatus
readArchive(CxArchive * archive,CxFP * fp)107 readArchive(CxArchive *archive, CxFP *fp)
108 {
109 ZipLocalHeader header;
110 ZipStatus status;
111 ZipExtra zip_extra;
112 int first_header_check = 1;
113 CxDirectory *root;
114 root = cxGetArchiveRoot(archive);
115
116 /* fprintf(stdout, "READING ZIP ARHIVE!\n"); */
117
118 while ((status = cxZipReadLocalHeader(&header, fp)) == ZIP_SUCCESS)
119 {
120
121 char *temp, *basePath = NULL, *baseName = NULL;
122 const char *headerName; /* Cause I copy Chippy! */
123 int nameLength;
124 ZipUnixExtra *unix_extra;
125 CxDirectory *parentDir;
126
127
128 if (*header.filename == '.' && header.filename[1] == '/')
129 headerName = header.filename + 1;
130 else
131 headerName = header.filename;
132
133 if (!strcmp(header.filename, "/"))
134 continue;
135
136 nameLength = strlen(headerName);
137 /* fprintf(stdout, " -- Reading header #%d\n", first_header_check);*/
138 /* Now check to see whether this is a file or a directory
139 * We rely on what appears to be a "/" at the end of any directory
140 * name, and a lack of "/" for a file.
141 */
142 if (headerName[nameLength - 1] == '/')
143 {
144 /* A directory! yay! */
145 CxDirectory *dir;
146 char *fullPath = strdup(headerName);
147
148 fullPath[nameLength - 1] = '\0'; /* Get rid of that trailing /, aww */
149 cxSplitPath(fullPath, &basePath, &baseName);
150
151 if (baseName != NULL && baseName[0] == '.' &&
152 baseName[1] == '\0')
153 {
154 free(baseName);
155 free(fullPath);
156 if (basePath != NULL)
157 free(basePath);
158
159 continue;
160 }
161
162 dir = cxNewDirectory();
163 cxSetDirName(dir, baseName);
164 /*
165 fprintf(stdout, " DIRECTORY\n");
166 fprintf(stdout, " ---Name: %s\n", baseName);
167 fprintf(stdout, " ---Path: %s\n", fullPath);
168 free(baseName);
169 free(fullPath);
170 */
171
172
173 /* Now add it to the correct parent dir */
174 if (basePath != NULL)
175 {
176 parentDir = cxGetDirectory(root, basePath);
177 free(basePath);
178 }
179 else
180 parentDir = root;
181
182 cxDirAddSubDir(parentDir, dir);
183
184 }
185 else
186 {
187 /* it's a file who's spoon is too big :-/ */
188 CxFile *file;
189 ZipFile *zipFile;
190
191 file = cxNewFile();
192
193 temp = cxGetBaseName(header.filename);
194 cxSetFileName(file, temp);
195 /*
196 fprintf(stdout, " FILE\n");
197 fprintf(stdout, " ---FileName: %s\n", temp);
198 fprintf(stdout, " ---Path: %s\n", header.filename);
199 free(temp);
200 */
201
202 cxSetFileSize(file, header.uncompressedSize);
203 cxSetFileDate(file, cxDosDateToUnix(header.mtime));
204 cxSetFileCompressedSize(file, header.compressedSize);
205
206 MEM_CHECK(zipFile = (ZipFile *)malloc(sizeof(ZipFile)));
207 zipFile->startOffset = header.fileOffset;
208 zipFile->curOffset = header.fileOffset;
209 zipFile->compression = header.compression;
210 zipFile->flag = header.flag;
211 switch(header.compression)
212 {
213 case ZIP_DEFLATED:
214 zipFile->callback = __inflateReadFunc;
215 break;
216 default:
217 /* All other unsupported compression methods */
218 zipFile->callback = __altReadFunc;
219 }
220 file->moduleData = zipFile;
221
222 zip_extra.field = header.extraField;
223 zip_extra.length = header.extraLength;
224 zip_extra.data = (ZipUnixExtra *)NULL;
225 /* Unix oriented -- maybe put in some other #ifdef WIN32 blah
226 * in the future to have ntfs or other extra type support
227 */
228 if (!cxZipFindExtra(&zip_extra, ZIP_PKWARE_UNIX_EXTRA))
229 {
230 cxZipFindExtra(&zip_extra, ZIP_UNIX2_IZ_EXTRA);
231 cxZipFindExtra(&zip_extra, ZIP_TSTAMP_EXTRA);
232 if(&zip_extra.data == NULL)
233 cxZipFindExtra(&zip_extra, ZIP_UNIX1_IZ_EXTRA);
234
235 }
236
237 if ((unix_extra = (ZipUnixExtra *)zip_extra.data) != NULL)
238 {
239 if (unix_extra->mtime > 0)
240 cxSetFileDate(file, (time_t)unix_extra->mtime);
241 if (unix_extra->uid > 0)
242 cxSetFileUid(file, (uid_t)unix_extra->uid);
243 if (unix_extra->gid > 0)
244 cxSetFileGid(file, (gid_t)unix_extra->gid);
245 /* TODO: Handle these links in the future */
246 if (unix_extra->link != NULL)
247 free(unix_extra->link);
248
249 free(unix_extra);
250 }
251
252 basePath = cxGetBasePath(header.filename);
253 if (basePath != NULL)
254 {
255 parentDir = cxGetDirectory(root, basePath);
256 free(basePath);
257 }
258 else
259 parentDir = root;
260
261 cxDirAddFile(parentDir, file);
262
263 }
264
265 /* Clean up the header data */
266 free(header.filename);
267 if (header.extraField != NULL)
268 free(header.extraField);
269
270 first_header_check++;
271 } /* end of while */
272
273 if (status == ZIP_ERROR && first_header_check == 1)
274 return CX_INVALID_FORMAT;
275 else
276 {
277 /* If we haven't stopped because we've reached the last file
278 * then something else is wrong
279 */
280 /*if (status != ZIP_LAST_FILE)
281 return CX_CORRUPT; */
282 }
283
284 cxSetArchiveType(archive, CX_ARCHIVE_MULTI);
285
286 /* Store the file pointer in moduleData */
287 archive->moduleData = fp;
288
289 return CX_SUCCESS;
290 }
291
292 static CxStatus
saveArchive(CxArchive * archive,CxFP * fp)293 saveArchive(CxArchive *archive, CxFP *fp)
294 {
295 return CX_NOT_SUPPORTED;
296 }
297
298 static void
closeArchive(CxArchive * archive)299 closeArchive(CxArchive *archive)
300 {
301 archive->moduleData = NULL;
302 }
303
304
305 static CxFP *
openFile(CxFile * file,CxAccessMode mode)306 openFile(CxFile *file, CxAccessMode mode)
307 {
308 ZipFile *fileData;
309 CxArchive *archive;
310 CxFP *fp;
311
312 if(!CX_IS_MODE_READ_ONLY(mode))
313 return NULL;
314
315 archive = cxGetFileArchive(file);
316 fp = cxNewFp();
317 fileData = file->moduleData;
318
319 cxSetReadFunc(fp, fileData->callback);
320 cxSetWriteFunc(fp, __writeFuncZip);
321 cxSetSeekFunc(fp, __seekFuncZip);
322 cxSetCloseFunc(fp, __closeFuncZip);
323
324 fp->moduleData = fileData;
325
326 cxSeek((CxFP *)archive->moduleData, fileData->startOffset, SEEK_SET);
327 __cxZipInflateInit2(file);
328
329 return fp;
330 }
331
332
333 static void
destroyFile(CxFile * file)334 destroyFile(CxFile *file)
335 {
336 if (file->moduleData != NULL)
337 {
338 free(file->moduleData);
339
340 file->moduleData = NULL;
341 }
342 }
343
344 static char
supportsExtension(const char * ext)345 supportsExtension(const char *ext)
346 {
347 if(!strcasecmp(ext, "zip"))
348 return 1;
349
350 return 0;
351 }
352
353 static CxArchiveOps ops =
354 {
355 readArchive,
356 saveArchive,
357 closeArchive,
358 openFile,
359 destroyFile,
360 supportsExtension
361 };
362
363 static void
__moduleInit(CxModuleType type)364 __moduleInit(CxModuleType type)
365 {
366 }
367
368 CX_INIT_ARCHIVE_MODULE(zip, __moduleInit, ops);
369