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