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