]> www.fi.muni.cz Git - evince.git/blob - impress/zip.c
4b179b55ef900ad53d88e71d05aad159eb6e1cb6
[evince.git] / impress / zip.c
1 /* imposter (OO.org Impress viewer)
2 ** Copyright (C) 2003-2005 Gurer Ozen
3 ** This code is free software; you can redistribute it and/or
4 ** modify it under the terms of GNU General Public License.
5 */
6
7 #include "common.h"
8 #include "zip.h"
9 #include <zlib.h>
10 #define _(x) x
11
12 enum {
13         ZIP_OK = 0,
14         ZIP_NOMEM,
15         ZIP_NOSIG,
16         ZIP_BADZIP,
17         ZIP_NOMULTI,
18         ZIP_EOPEN,
19         ZIP_EREAD,
20         ZIP_NOFILE
21 };
22
23 struct zipfile {
24         struct zipfile *next;
25         char *name;
26         ulong crc;
27         ulong zip_size;
28         ulong real_size;
29         ulong pos;
30 };
31
32 struct zip_struct {
33         FILE *f;
34         struct zipfile *files;
35         ulong cd_pos;
36         ulong cd_size;
37         ulong cd_offset;
38         ulong head_size;
39         ulong rem_size;
40         ulong nr_files;
41 };
42
43 char *
44 zip_error (int err)
45 {
46         char *ret;
47
48         switch (err) {
49                 case ZIP_OK:
50                         ret = _("No error");
51                         break;
52                 case ZIP_NOMEM:
53                         ret = _("Not enough memory");
54                         break;
55                 case ZIP_NOSIG:
56                         ret = _("Cannot find zip signature");
57                         break;
58                 case ZIP_BADZIP:
59                         ret = _("Invalid zip file");
60                         break;
61                 case ZIP_NOMULTI:
62                         ret = _("Multi file zips are not supported");
63                         break;
64                 case ZIP_EOPEN:
65                         ret = _("Cannot open the file");
66                         break;
67                 case ZIP_EREAD:
68                         ret = _("Cannot read data from file");
69                         break;
70                 case ZIP_NOFILE:
71                         ret = _("Cannot find file in the zip archive");
72                         break;
73                 default:
74                         ret = _("Unknown error");
75                         break;
76         }
77         return ret;
78 }
79
80 static int
81 find_cd (zip *z)
82 {
83         FILE *f;
84         char *buf;
85         ulong size, pos, i, flag;
86
87         f = z->f;
88         if (fseek (f, 0, SEEK_END) != 0) return 1;
89         size = ftell (f);
90         if (size < 0xffff) pos = 0; else pos = size - 0xffff;
91         buf = malloc (size - pos + 1);
92         if (!buf) return 1;
93         if (fseek (f, pos, SEEK_SET) != 0) {
94                 free (buf);
95                 return 1;
96         }
97         if (fread (buf, size - pos, 1, f) != 1) {
98                 free (buf);
99                 return 1;
100         }
101         flag = 0;
102         for (i = size - pos - 3; i > 0; i--) {
103                 if (buf[i] == 0x50 && buf[i+1] == 0x4b && buf[i+2] == 0x05 && buf[i+3] == 0x06) {
104                         z->cd_pos = i + pos;
105                         flag = 1;
106                         break;
107                 }
108         }
109         free (buf);
110         if (flag != 1) return 1;
111         return 0;
112 }
113
114 static unsigned long
115 get_long (unsigned char *buf)
116 {
117         return buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
118 }
119
120 static unsigned long
121 get_word (unsigned char *buf)
122 {
123         return buf[0] + (buf[1] << 8);
124 }
125
126 static int
127 list_files (zip *z)
128 {
129         unsigned char buf[46];
130         struct zipfile *zfile;
131         ulong pat, fn_size;
132         int nr = 0;
133
134         pat = z->cd_offset;
135         while (nr < z->nr_files) {
136                 fseek (z->f, pat + z->head_size, SEEK_SET);
137
138                 if (fread (buf, 46, 1, z->f) != 1) return ZIP_EREAD;
139                 if (get_long (buf) != 0x02014b50) return ZIP_BADZIP;
140
141                 zfile = malloc (sizeof (struct zipfile));
142                 if (!zfile) return ZIP_NOMEM;
143                 memset (zfile, 0, sizeof (struct zipfile));
144
145                 zfile->crc = get_long (buf + 16);
146                 zfile->zip_size = get_long (buf + 20);
147                 zfile->real_size = get_long (buf + 24);
148                 fn_size = get_word (buf + 28);
149                 zfile->pos = get_long (buf + 42);
150
151                 zfile->name = malloc (fn_size + 1);
152                 if (!zfile->name) {
153                         free (zfile);
154                         return ZIP_NOMEM;
155                 }
156                 fread (zfile->name, fn_size, 1, z->f);
157                 zfile->name[fn_size] = '\0';
158
159                 zfile->next = z->files;
160                 z->files = zfile;
161
162                 pat += 0x2e + fn_size + get_word (buf + 30) + get_word (buf + 32);
163                 nr++;
164         }
165         return ZIP_OK;
166 }
167
168 zip *
169 zip_open (const char *fname, int *err)
170 {
171         unsigned char buf[22];
172         zip *z;
173         FILE *f;
174
175         f = fopen (fname, "rb");
176         if (NULL == f) {
177                 *err = ZIP_EOPEN;
178                 return NULL;
179         }
180
181         z = malloc (sizeof (zip));
182         memset (z, 0, sizeof (zip));
183         z->f = f;
184
185         if (find_cd (z)) {
186                 zip_close (z);
187                 *err = ZIP_NOSIG;
188                 return NULL;
189         }
190
191         fseek (f, z->cd_pos, SEEK_SET);
192         if (fread (buf, 22, 1, f) != 1) {
193                 zip_close (z);
194                 *err = ZIP_EREAD;
195                 return NULL;
196         }
197         z->nr_files = get_word (buf + 10);
198         if (get_word (buf + 8) != z->nr_files) {
199                 zip_close (z);
200                 *err =  ZIP_NOMULTI;
201                 return NULL;
202         }
203         z->cd_size = get_long (buf + 12);
204         z->cd_offset = get_long (buf + 16);
205         z->rem_size = get_word (buf + 20);
206         z->head_size = z->cd_pos - (z->cd_offset + z->cd_size);
207
208         *err = list_files (z);
209         if (*err != ZIP_OK) {
210                 zip_close (z);
211                 return NULL;
212         }
213
214         *err = ZIP_OK;
215         return z;
216 }
217
218 void
219 zip_close (zip *z)
220 {
221         struct zipfile *zfile, *tmp;
222
223         zfile = z->files;
224         while (zfile) {
225                 tmp = zfile->next;
226                 if (zfile->name) free (zfile->name);
227                 free (zfile);
228                 zfile = tmp;
229         }
230         z->files = NULL;
231         if (z->f) fclose (z->f);
232         z->f = NULL;
233 }
234
235 static struct zipfile *
236 find_file (zip *z, const char *name)
237 {
238         struct zipfile *zfile;
239
240         zfile = z->files;
241         while (zfile) {
242                 if (strcmp (zfile->name, name) == 0) return zfile;
243                 zfile = zfile->next;
244         }
245         return NULL;
246 }
247
248 static int
249 seek_file (zip *z, struct zipfile *zfile)
250 {
251         unsigned char buf[30];
252
253         fseek (z->f, zfile->pos + z->head_size, SEEK_SET);
254         if (fread (buf, 30, 1, z->f) != 1) return ZIP_EREAD;
255         if (get_long (buf) != 0x04034b50) return ZIP_BADZIP;
256         fseek (z->f, get_word (buf + 26) + get_word (buf + 28), SEEK_CUR);
257         return ZIP_OK;
258 }
259
260 iks *
261 zip_load_xml (zip *z, const char *name, int *err)
262 {
263         iksparser *prs;
264         char *real_buf;
265         iks *x;
266         struct zipfile *zfile;
267
268         *err = ZIP_OK;
269
270         zfile = find_file (z, name);
271         if (!zfile) {
272                 *err = ZIP_NOFILE;
273                 return NULL;
274         }
275
276         seek_file (z, zfile);
277
278         real_buf = malloc (zfile->real_size + 1);
279         if (zfile->zip_size < zfile->real_size) {
280                 char *zip_buf;
281                 z_stream zs;
282                 zs.zalloc = NULL;
283                 zs.zfree = NULL;
284                 zs.opaque = NULL;
285                 zip_buf = malloc (zfile->zip_size);
286                 fread (zip_buf, zfile->zip_size, 1, z->f);
287                 zs.next_in = zip_buf;
288                 zs.avail_in = zfile->zip_size;
289                 zs.next_out = real_buf;
290                 zs.avail_out = zfile->real_size;
291                 inflateInit2 (&zs, -MAX_WBITS);
292                 inflate (&zs, Z_FINISH);
293                 inflateEnd (&zs);
294                 free (zip_buf);
295         } else {
296                 fread (real_buf, zfile->real_size, 1, z->f);
297         }
298
299         real_buf[zfile->real_size] = '\0';
300         prs = iks_dom_new (&x);
301         iks_parse (prs, real_buf, zfile->real_size, 1);
302         iks_parser_delete (prs);
303         free (real_buf);
304         return x;
305 }
306
307 unsigned long zip_get_size (zip *z, const char *name)
308 {
309         struct zipfile *zf;
310
311         zf = find_file (z, name);
312         if (!zf) return 0;
313         return zf->real_size;
314 }
315
316 int zip_load (zip *z, const char *name, char *buf)
317 {
318         struct zipfile *zfile;
319
320         zfile = find_file (z, name);
321         if (!zfile) return ZIP_NOFILE;
322
323         seek_file (z, zfile);
324
325         if (zfile->zip_size < zfile->real_size) {
326                 char *zip_buf;
327                 z_stream zs;
328                 zs.zalloc = NULL;
329                 zs.zfree = NULL;
330                 zs.opaque = NULL;
331                 zip_buf = malloc (zfile->zip_size);
332                 fread (zip_buf, zfile->zip_size, 1, z->f);
333                 zs.next_in = zip_buf;
334                 zs.avail_in = zfile->zip_size;
335                 zs.next_out = buf;
336                 zs.avail_out = zfile->real_size;
337                 inflateInit2 (&zs, -MAX_WBITS);
338                 inflate (&zs, Z_FINISH);
339                 inflateEnd (&zs);
340                 free (zip_buf);
341         } else {
342                 fread (buf, zfile->real_size, 1, z->f);
343         }
344
345         return ZIP_OK;
346 }