1 /*
2   Additional tools for Minizip
3   Code: Xavier Roche '2004
4   License: Same as ZLIB (www.gzip.org)
5 */
6 
7 /* Code */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include "zlib.h"
12 #include "unzip.h"
13 
14 #define READ_8(adr)  ((unsigned char)*(adr))
15 #define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
16 #define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
17 
18 #define WRITE_8(buff, n) do { \
19   *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
20 } while(0)
21 #define WRITE_16(buff, n) do { \
22   WRITE_8((unsigned char*)(buff), n); \
23   WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
24 } while(0)
25 #define WRITE_32(buff, n) do { \
26   WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
27   WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
28 } while(0)
29 
30 extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered)
31 const char* file;
32 const char* fileOut;
33 const char* fileOutTmp;
34 uLong* nRecovered;
35 uLong* bytesRecovered;
36 {
37   int err = Z_OK;
38   FILE* fpZip = fopen(file, "rb");
39   FILE* fpOut = fopen(fileOut, "wb");
40   FILE* fpOutCD = fopen(fileOutTmp, "wb");
41   if (fpZip != NULL &&  fpOut != NULL) {
42     int entries = 0;
43     uLong totalBytes = 0;
44     char header[30];
45     char filename[1024];
46     char extra[1024];
47     int offset = 0;
48     int offsetCD = 0;
49     while ( fread(header, 1, 30, fpZip) == 30 ) {
50       int currentOffset = offset;
51 
52       /* File entry */
53       if (READ_32(header) == 0x04034b50) {
54         unsigned int version = READ_16(header + 4);
55         unsigned int gpflag = READ_16(header + 6);
56         unsigned int method = READ_16(header + 8);
57         unsigned int filetime = READ_16(header + 10);
58         unsigned int filedate = READ_16(header + 12);
59         unsigned int crc = READ_32(header + 14); /* crc */
60         unsigned int cpsize = READ_32(header + 18); /* compressed size */
61         unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
62         unsigned int fnsize = READ_16(header + 26); /* file name length */
63         unsigned int extsize = READ_16(header + 28); /* extra field length */
64         filename[0] = extra[0] = '\0';
65 
66         /* Header */
67         if (fwrite(header, 1, 30, fpOut) == 30) {
68           offset += 30;
69         } else {
70           err = Z_ERRNO;
71           break;
72         }
73 
74         /* Filename */
75         if (fnsize > 0) {
76           if (fnsize < sizeof(filename)) {
77             if (fread(filename, 1, fnsize, fpZip) == fnsize) {
78                 if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
79                 offset += fnsize;
80               } else {
81                 err = Z_ERRNO;
82                 break;
83               }
84             } else {
85               err = Z_ERRNO;
86               break;
87             }
88           } else {
89             err = Z_ERRNO;
90             break;
91           }
92         } else {
93           err = Z_STREAM_ERROR;
94           break;
95         }
96 
97         /* Extra field */
98         if (extsize > 0) {
99           if (extsize < sizeof(extra)) {
100             if (fread(extra, 1, extsize, fpZip) == extsize) {
101               if (fwrite(extra, 1, extsize, fpOut) == extsize) {
102                 offset += extsize;
103                 } else {
104                 err = Z_ERRNO;
105                 break;
106               }
107             } else {
108               err = Z_ERRNO;
109               break;
110             }
111           } else {
112             err = Z_ERRNO;
113             break;
114           }
115         }
116 
117         /* Data */
118         {
119           int dataSize = cpsize;
120           if (dataSize == 0) {
121             dataSize = uncpsize;
122           }
123           if (dataSize > 0) {
124             char* data = malloc(dataSize);
125             if (data != NULL) {
126               if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
127                 if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
128                   offset += dataSize;
129                   totalBytes += dataSize;
130                 } else {
131                   err = Z_ERRNO;
132                 }
133               } else {
134                 err = Z_ERRNO;
135               }
136               free(data);
137               if (err != Z_OK) {
138                 break;
139               }
140             } else {
141               err = Z_MEM_ERROR;
142               break;
143             }
144           }
145         }
146 
147         /* Central directory entry */
148         {
149           char header[46];
150           char* comment = "";
151           int comsize = (int) strlen(comment);
152           WRITE_32(header, 0x02014b50);
153           WRITE_16(header + 4, version);
154           WRITE_16(header + 6, version);
155           WRITE_16(header + 8, gpflag);
156           WRITE_16(header + 10, method);
157           WRITE_16(header + 12, filetime);
158           WRITE_16(header + 14, filedate);
159           WRITE_32(header + 16, crc);
160           WRITE_32(header + 20, cpsize);
161           WRITE_32(header + 24, uncpsize);
162           WRITE_16(header + 28, fnsize);
163           WRITE_16(header + 30, extsize);
164           WRITE_16(header + 32, comsize);
165           WRITE_16(header + 34, 0);     /* disk # */
166           WRITE_16(header + 36, 0);     /* int attrb */
167           WRITE_32(header + 38, 0);     /* ext attrb */
168           WRITE_32(header + 42, currentOffset);
169           /* Header */
170           if (fwrite(header, 1, 46, fpOutCD) == 46) {
171             offsetCD += 46;
172 
173             /* Filename */
174             if (fnsize > 0) {
175               if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
176                 offsetCD += fnsize;
177               } else {
178                 err = Z_ERRNO;
179                 break;
180               }
181             } else {
182               err = Z_STREAM_ERROR;
183               break;
184             }
185 
186             /* Extra field */
187             if (extsize > 0) {
188               if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
189                 offsetCD += extsize;
190               } else {
191                 err = Z_ERRNO;
192                 break;
193               }
194             }
195 
196             /* Comment field */
197             if (comsize > 0) {
198               if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
199                 offsetCD += comsize;
200               } else {
201                 err = Z_ERRNO;
202                 break;
203               }
204             }
205 
206 
207           } else {
208             err = Z_ERRNO;
209             break;
210           }
211         }
212 
213         /* Success */
214         entries++;
215 
216       } else {
217         break;
218       }
219     }
220 
221     /* Final central directory  */
222     {
223       int entriesZip = entries;
224       char header[22];
225       char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
226       int comsize = (int) strlen(comment);
227       if (entriesZip > 0xffff) {
228         entriesZip = 0xffff;
229       }
230       WRITE_32(header, 0x06054b50);
231       WRITE_16(header + 4, 0);    /* disk # */
232       WRITE_16(header + 6, 0);    /* disk # */
233       WRITE_16(header + 8, entriesZip);   /* hack */
234       WRITE_16(header + 10, entriesZip);  /* hack */
235       WRITE_32(header + 12, offsetCD);    /* size of CD */
236       WRITE_32(header + 16, offset);      /* offset to CD */
237       WRITE_16(header + 20, comsize);     /* comment */
238 
239       /* Header */
240       if (fwrite(header, 1, 22, fpOutCD) == 22) {
241 
242         /* Comment field */
243         if (comsize > 0) {
244           if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
245             err = Z_ERRNO;
246           }
247         }
248 
249       } else {
250         err = Z_ERRNO;
251       }
252     }
253 
254     /* Final merge (file + central directory) */
255     fclose(fpOutCD);
256     if (err == Z_OK) {
257       fpOutCD = fopen(fileOutTmp, "rb");
258       if (fpOutCD != NULL) {
259         int nRead;
260         char buffer[8192];
261         while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
262           if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
263             err = Z_ERRNO;
264             break;
265           }
266         }
267         fclose(fpOutCD);
268       }
269     }
270 
271     /* Close */
272     fclose(fpZip);
273     fclose(fpOut);
274 
275     /* Wipe temporary file */
276     (void)remove(fileOutTmp);
277 
278     /* Number of recovered entries */
279     if (err == Z_OK) {
280       if (nRecovered != NULL) {
281         *nRecovered = entries;
282       }
283       if (bytesRecovered != NULL) {
284         *bytesRecovered = totalBytes;
285       }
286     }
287   } else {
288     err = Z_STREAM_ERROR;
289   }
290   return err;
291 }
292